88

In perl, you can exit with an error msg with die "some msg". Is there an equivalent single command in bash? Right now, I'm achieving this using commands: echo "some msg" && exit 1

Eddy Freddy
  • 1,820
  • 1
  • 13
  • 18
PJx
  • 1,043
  • 1
  • 9
  • 10
  • 2
    See [BashFAQ/101 - Common utility functions (warn, die)](https://mywiki.wooledge.org/BashFAQ/101). – pjh Jun 18 '22 at 23:06

5 Answers5

97

You can roll your own easily enough:

die() { echo "$*" 1>&2 ; exit 1; }
...
die "Kaboom"
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 1
    +1; but note that die with no arguments emits an unwanted newline. I like '{ test -n "$@" && echo "$@"; exit 1; } >&2' – William Pursell Oct 24 '11 at 11:55
  • 5
    @WilliamPursell: You could use `printf %s "${@+$@$'\n'}"` - In other words, print the arguments followed by a newline if they exist. – l0b0 May 14 '12 at 15:20
  • Looking back at this answer 6 years later, I realize that `"$*"` probably makes more sense than `"$@"`. The behavior will be identical in most cases, but differs for `die -n Oops` (not that you'd want to do that). – Keith Thompson Nov 01 '17 at 22:17
  • This works well, but with some caveats: Using this inside a script that is sourced from outside will not exit from this script, but from the script that called it. Similarly, calling it from a function will terminate the whole script, not just the function. I have not yet found a way to make `die` work in functions or sourced scripts... Using `return 1` can help if `set -e` is active, but even calling the function in a compound statement will disable `set -e` and all `die` calls within the script! – SvenS Jan 04 '23 at 12:29
34

Here's what I'm using. It's too small to put in a library so I must have typed it hundreds of times ...

warn () {
    echo "$0:" "$@" >&2
}
die () {
    rc=$1
    shift
    warn "$@"
    exit $rc
}

Usage: die 127 "Syntax error"

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
tripleee
  • 175,061
  • 34
  • 275
  • 318
19

This is a very close function to perl's "die" (but with function name):

function die
{
    local message=$1
    [ -z "$message" ] && message="Died"
    echo "$message at ${BASH_SOURCE[1]}:${FUNCNAME[1]} line ${BASH_LINENO[0]}." >&2
    exit 1
}

And bash way of dying if built-in function is failed (with function name)

function die
{
    local message=$1
    [ -z "$message" ] && message="Died"
    echo "${BASH_SOURCE[1]}: line ${BASH_LINENO[0]}: ${FUNCNAME[1]}: $message." >&2
    exit 1
}

So, Bash is keeping all needed info in several environment variables:

  • LINENO - current executed line number
  • FUNCNAME - call stack of functions, first element (index 0) is current function, second (index 1) is function that called current function
  • BASH_LINENO - call stack of line numbers, where corresponding FUNCNAME was called
  • BASH_SOURCE - array of source file, where corresponfing FUNCNAME is stored
rofrol
  • 14,438
  • 7
  • 79
  • 77
Sergey Irisov
  • 468
  • 4
  • 7
  • 7
    You could avoid the local variable altogether with `${1-Died}` -- with that, you can even pass in an empty string and it will work as you would expect. – tripleee Oct 28 '15 at 07:44
5

Yep, that's pretty much how you do it.

You might use a semicolon or newline instead of &&, since you want to exit whether or not echo succeeds (though I'm not sure what would make it fail).

Programming in a shell means using lots of little commands (some built-in commands, some tiny programs) that do one thing well and connecting them with file redirection, exit code logic and other glue.

It may seem weird if you're used to languages where everything is done using functions or methods, but you get used to it.

bonkydog
  • 2,012
  • 20
  • 11
  • Now I'm finding out another unpleasant effect. While the echo command is being executed, the interpreter doesn't wait for it to finish, but simultaneously executes the code below it! Here's what I have: `CMD1 || (echo 'ERROR MSG'; exit 1); CMD2; CMD3`. Now, if CMD1 fails, I expect the echo and exit to take place, and the script will exit. But now when CMD1 fails, CMD2 and CMD3 are actually executed before the echo. This is according to the error log. Bizarre or not? – PJx Oct 23 '11 at 23:12
  • actually, I think the parentheses () may be the culprit. Just read somewhere that they may execute the code inside in a subshell, which would of course not have the desired effect. I just changed my code to a if-fi block, and it worked as expected. Just for my curiosity, can I combine this code into a one liner? – PJx Oct 23 '11 at 23:29
  • 2
    Use `{}` instead of `()` to avoid the subshell problem. Also, it's a good idea to send errors to stderr instead of stdout: `CMD1 || { echo 'ERROR MSG' >&2; exit 1; }; CMD2; CMD3` – Gordon Davisson Oct 24 '11 at 01:14
0
# echo pass params and print them to a log file
wlog(){
    # check terminal if exists echo 
    test -t 1 && echo "`date +%Y.%m.%d-%H:%M:%S` [$$] $*"
    # check LogFile and 
    test -z $LogFile || {
      echo "`date +%Y.%m.%d-%H:%M:%S` [$$] $*" >> $LogFile
    } #eof test
 } 
# eof function wlog 


# exit with passed status and message
Exit(){
    ExitStatus=0
    case $1 in
      [0-9]) ExitStatus="$1"; shift 1;;
  esac
    Msg="$*"
    test "$ExitStatus" = "0" || Msg=" ERROR: $Msg : $@"
    wlog " $Msg"
    exit $ExitStatus
}
#eof function Exit
Yordan Georgiev
  • 5,114
  • 1
  • 56
  • 53