exit from running, sourcing, or pasting a series of commands in bash

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
PS2="exit required, press ctrl-C to abort "
alias EXIT="echo -e 'e[Aexit required, terminatinge[0K' ; return 2 >&/dev/null || 
    [[ '${0}' == '${BASH_SOURCE[0]}' ]] && exit 2 || 
    cat >&/dev/null <<EODATA"

# usage
echo "good stuff"           # commands that should pass
false || EXIT               # example of a 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.

  1. If sourcing the script, e.g., source 1.sh, the return 2 in the alias causes the sourcing to stop.

  2. If running the script directly, e.g., bash 1.sh or just ./1.sh, the return 2 fails 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 an exit 2.

  3. If pasting, these first two conditions really don’t matter – it begins a here-string command that is never completed. This is done to “eat” any remaining pasted commands. Normally, the here-string gives a prompt of PS2, so we override that to be the prompt we want the user to see. When the user presses ctrl-C, it breaks out of the here-string command and returns to the console prompt.

However, in the first two cases, bash never finds the here-string ending and emits an error message when the alias is called. To work around this, we emit an escape code to move the cursor up, output its own error message, then delete to the end of the line to erase bash’s error message.

This relies on ANSI escape trickery and generally feels fragile. It also changes the user’s PS2 prompt in the current session. It requires user action to exit from the pasted commands. 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.