I'm writing a script that I run directly, "source" from the bash console, or cut/paste as a sequence of commands into a console from an editor window. The script or its pasted commands may encounter an error and need to exit; when it does, I do not want to exit the console.
The problem is pasting commands into the terminal because each line is treated independently: aborting on one line will still execute the subsequent lines. I do not want to rely on external commands, only Bash.
I have come up with a way to do this, but am wondering if there's an easier/better way. Put this into a script then either run it, source, it, or paste the commands into a console window:
#!/bin/bash # test of exit from running, sourcing, or pasting a series of commands # return fails implies either running or pasting; if not running, then pasting shopt -s expand_aliases SAVEOPTS=/tmp/$$.bashopts saveopts () { set | egrep -v '(BASHOPTS|BASH_VERSINFO|EUID|PPID|SHELLOPTS|UID)=' >$SAVEOPTS alias >>$SAVEOPTS } alias EXIT="{ echo 'exit required, terminating' ; \ return 2 >&/dev/null || \ [[ '${0}' == '${BASH_SOURCE[0]}' ]] && exit 2; \ saveopts ; exec bash --rcfile $SAVEOPTS; }" # usage echo "good stuff" # commands that should pass true || EXIT # example of a successful command echo "more good stuff" # another good command false || EXIT # failed command echo "SHOULD NOT SEE THIS" # do not want this to run I'm using this in GNU bash, version 5.2.15(1).
How it works - call EXIT when you wish to terminate.
If sourcing the script, e.g.,
source 1.sh, thereturn 2in the alias causes the sourcing to stop.If running the script directly, e.g.,
bash 1.shor just./1.sh, thereturn 2fails and falls through to the second condition that checks whether the name of the process is the name of the script. This succeeds, resulting in anexit 2.If pasting, these first two conditions don't take effect, so instead it re-execs the current shell bringing back in the previous settings. Using exec preserves the PID of the shell. (The grep -v's are for unsettable options and is done to prevent the shell from trying to set these.)
This generally feels fragile. Is there a better way?
I'd like to have EXIT as the command, but am open to eval something, or a function call, or something else, as long as it's relatively short. I also prefer not to depend on external commands (which I don't have installed) such as xclip.
Revisions to this post:
- Added the exec logic simplifying the entire thing
- This is a different question than another question having to do with aliases and not terminating a list of pasted commands.
set -e{ ...; }: i.sstatic.net/KnStNfnG.png, and the entire block gets cancelled at once. This wouldn't work if bracketed paste is disabled and each line gets treated as if its input individually.