Remix.run Logo
o11c 3 days ago

FWIW, I've grown the following which handles a few more cases. For some reason I wasn't aware of `caller` ...

    set -e

    is-oil()
    {
        test -n "$OIL_VERSION"
    }

    set -E || is-oil

    trap 'echo "$BASH_SOURCE:$LINENO: error: failure during early startup! Details unavailable."' ERR

    magic_exitvalue=$(($(kill -l CONT)+128))

    backtrace()
    {
        {
            local status=$?
            if [ "$status" -eq "$magic_exitvalue" ]
            then
                echo '(omit backtrace)'
                exit "$magic_exitvalue"
            fi
            local max file line func argc argvi i j
            echo
            echo 'Panic! Something failed unexpectedly.' "(status $status)"
            echo 'While executing' "$BASH_COMMAND"
            echo
            echo Backtrace:
            echo
            max=${#BASH_LINENO[@]}
            let max-- # The top-most frame is "special".
            argvi=${BASH_ARGC[0]}
            for ((i=1;i<max;++i))
            do
                file=${BASH_SOURCE[i]}
                line=${BASH_LINENO[i-1]}
                func=${FUNCNAME[i]}
                argc=${BASH_ARGC[i]}
                printf '%s:%d: ... in %q' "$file" "$line" "$func"
                # BASH_ARGV: ... bar foo ...
                # argvi          ^
                # argvi+argc             ^
                for ((j=argc-1; j>=0; --j))
                do
                    printf ' %q' ${BASH_ARGV[argvi+j]}
                done
                let argvi+=argc || true
                printf '\n'
            done

            if true
            then
                file=${BASH_SOURCE[i]}
                line=${BASH_LINENO[i-1]}
                printf '%s:%d: ... at top level\n' "$file" "$line"
            fi
        } >&2
        exit "$magic_exitvalue"
        unreachable
    }
    shopt -s extdebug
    trap 'backtrace' ERR
edoceo 3 days ago | parent | next [-]

What the hell. This is cool and all but I'm looking at it as a signal I should move up one tier in language (eg: to Perl, PHP, Python or Ruby)

0xbadcafebee 3 days ago | parent | next [-]

Or go the other direction: stop trying to do fancy things and write simpler code that avoids errors.

  #!/bin/sh
  [ "${DEBUG:-0}" = "1" ] && set -x
  set -u
  foo="$( my-external-program | pipe1 | pipe2 | pipe3 )"
  if [ -z "$foo" ] ; then
      echo "Error: I didn't get any output; exiting!"
      exit 1
  fi
  echo "Well I got something back. Was it right?"
  if ! printf "%s\n" "$foo" | grep -q -E 'some-extended-regex' ; then
      echo "Error: '$foo' didn't match what I was looking for; exiting!"
      exit 1
  fi
  echo "Do the thing now..."
A lot of programs will either produce valid output as STDOUT, or if they encounter an error, not produce STDOUT. So for the most part you just need to 1) look for any STDOUT at all, and then 2) filter it for the specific output you're looking for. For anything else, just die with an error. If you need to find out why it didn't run, re-run with DEBUG=1.

Advanced diagnosis code won't make your program work better, but it will make it more complicated. Re-running with tracing enabled works just as well 99% of the time.

maccard 3 days ago | parent | next [-]

> A lot of programs will either produce valid output as STDOUT, or if they encounter an error not produce stdout

Lots of programs produce nothing in the success case and only print in the failure case.

3 days ago | parent [-]
[deleted]
o11c 3 days ago | parent | prev [-]

Ah yes, the old "just write non-buggy code, or at least code whose bugs are deterministic".

My solution just silently sits in the background for unexpected, unpredictable bugs.

o11c 3 days ago | parent | prev [-]

I actually tried rewriting this in Python, but gave up since Python's startup latency is atrocious if you have even a few imports (and using a socket to a pre-existing server is fundamentally unable to preserve enough process context related to the terminal). Perl would probably be a better fit but it's $CURRENTYEAR and I've managed to avoid learning Perl every year so far, and I don't want to break my streak just for this.

The Bash code is not only fast but pretty easy to understand (other than perhaps the header, which I never have to change).

dataflow 3 days ago | parent | next [-]

PHP maybe? Or in limited cases, AWK? But I'd definitely learn Perl, it's a gem.

oguz-ismail 3 days ago | parent [-]

PHP needs to be installed, Perl is dead and AWK is more limited than Bash

ZYbCRq22HbJ2y7 3 days ago | parent | next [-]

> PHP needs to be installed

There are some ways around this:

https://github.com/crazywhalecc/static-php-cli

macintux 3 days ago | parent | prev [-]

For what little it’s worth, Perl is very much not dead.

Fire-Dragon-DoL 3 days ago | parent | prev [-]

Maybe try ruby, or you could use go (yeah, have to compile)

chubot 3 days ago | parent | prev [-]

I think you should be able to get rid of the is-oil part, because set -E was implemented last year

    $ osh -c 'set -E; set -o |grep errtrace'
    set -o errtrace
I'd be interested in any bug reports if it doesn't behave the same way

(The Oils runtime supports FUNCNAME BASH_SOURCE and all that, but there is room for a much better introspection API. It actually has a JSON crash report with a shell stack dump, but it probably needs some polish.)

oguz-ismail 3 days ago | parent [-]

>I'd be interested in any bug reports

What's the point? You can't fix them anyway

jamesmiller5 3 days ago | parent [-]

They meant Oil(s) as in fixing bugs in the bash compatible replacement that they author for the OP's 'is-oil' check.

oguz-ismail 3 days ago | parent [-]

I know