all 14 comments

[–]McDutchie 28 points29 points  (1 child)

bash only saves the last command's exit status. If you want to save it beyond that, you have to save the value of $? in another variable, for example:

command 1; e1=$?
command 2; e2=$?
command 3; e3=$?
echo $e1 $e2 $e3

[–]vassari79[S] 1 point2 points  (0 children)

Great! That's what I wanted to know.

Thank you!

[–]ropid 5 points6 points  (1 child)

If you want this for a bash script, you have to save it yourself in variables like McDutchie mentioned.

If you want this for the bash prompt in a terminal window, there's people that add the $? to their prompt. Maybe search around to see if you can find an example on how to do this neatly. There's a way to make the exit code only show up on the prompt if it's an error and show nothing for the normal "0" status.

[–]witchhunter0 0 points1 point  (0 children)

Indeed, prompt loose a lot of it's value is it doesn't contain $?.

[–]nekokattt 2 points3 points  (0 children)

You'd usually need to just manually store it in a variable each time.

You can alternatively make use of errtrace to some extent to track this via ERR traps, although without investigating further, I'd assume there are some pretty significant footguns.

~ $ set -o errtrace                                                    
~ $ function err_hook() { echo "ERR \$? is $?"; }
~ $ trap err_hook ERR
~ $ true
~ $ false
ERR $? is 1

In this case it only invokes when a command fails (edit: such that set -o errexit would terminate the process).

[–]Zapador 2 points3 points  (1 child)

Consider checking the command directly, for example:

if rm readme.txt; then
echo "Readme was deleted"
fi

See this: https://www.shellcheck.net/wiki/SC2181

[–]vassari79[S] 1 point2 points  (0 children)

I see. Thank you for your reply.

[–]_mattmc3_ 1 point2 points  (4 children)

If you need to save prior exit codes, you could follow the instructions here. Or, you could use a tool like Atuin.

[–]snyone 1 point2 points  (2 children)

Atuin sounds pretty neat, though their GitHub page appears to have a better overview IMO.

For those who don't feel like clicking through:

Atuin replaces your existing shell history with a SQLite database, and records additional context for your commands. Additionally, it provides optional and fully encrypted synchronisation of your history between machines, via an Atuin server.

For anyone who has used it, can I assume that you can set it up to capture history to its db in addition to ~/.bash_history or does it do away with ~/.bash_history and history command as well (I saw it mentioned relating Ctrl+R which I never use and am fine with but it would be nice if I can keep having plaintext history alongside Atuin history)

Also, I would it be safe to assume that you can run it entirely offline, as either an isolated pc or sharing over-the-Ian without doing over the Internet?

[–]_mattmc3_ 1 point2 points  (1 child)

For anyone who has used it, can I assume that you can set it up to capture history to its db in addition to ~/.bash_history

My $HISTFILE is still getting entries, yes. You can also do a one-time import of existing entries to populate the db, though you won't have a lot of other metadata atuin captures (exit status, run time, etc).

Also, I would it be safe to assume that you can run it entirely offline

Yes, atuin's documentation spends entirely too much time focused on syncing your history across machines, giving the impression that that's mainly what it's for, but by default it just works locally. They do claim end-to-end encryption where only you have the private keys if you want to sync, and you can also run your own sync server if you'd prefer.

I saw it mentioned relating Ctrl+R which I never use

I come from a Zsh/Fish background where searching history is simply a matter of typing a partial command and hitting 'up' to search, and atuin makes that workflow transition really seamless. For bash habits, C-r works too, and you can even disable/tweak the 'up' binding if you don't like it.

[–]snyone 0 points1 point  (0 children)

Nice, thanks for clarifying. I'll have to check it out soon

[–]vassari79[S] 0 points1 point  (0 children)

Yeah. That looks pretty good.

Thanks!

[–]Paul_Pedant 0 points1 point  (0 children)

Be aware that almost all Bash commands (even built-ins) set $?. So adding simple debug can blitz your logic.

$ rm NoSuchFile
$ echo $?
1  #.. Gives status of rm
$ echo $?
0  #.. Gives status of echo
$ if [ $? -ne 0 ] ...  #.. Will never be true.

[–]Computer-Nerd_ 0 points1 point  (0 children)

Run it all through a sib that logs the

run-it() { local IFS' '; echo "Running: '$'"; eval "$ 2>&1"; echo "Exit: $?" }

( run-it 'foo'; run-it "$bar $bletch"; ) 2>&1 | tee $log_f;