Bash Shebang for dummies?
Solution 1
If a script /path/to/foo
begins with #!/bin/bash
, then executing /path/to/foo arg1 arg2
is equivalent to executing /bin/bash /path/too/foo arg1 arg2
. If the shebang line is #!/bin/bash -ex
, it is equivalent to executing /bin/bash -ex /path/too/foo arg1 arg2
. This feature is managed by the kernel.
Note that you can portably have only one argument on the shebang line: some unices (such as Linux) only accept one argument, so that #!/bin/bash -e -x
would lead to bash receiving the single five-character argument -e -x
(a syntax error) rather than two arguments -e
and -x
.
For the Bourne shell sh
and derived shells such as POSIX sh, bash, ksh, and zsh:
-e
means that if any command fails (which it indicates by returning a nonzero status), the script will terminate immediately.-x
causes the shell to print an execution trace.
Other programs may understand these options but with different meanings.
Solution 2
They are options passed to bash
see help set
for more info, in this case:
-x Print commands and their arguments as they are executed.
-e Exit immediately if a command exits with a non-zero status.
Solution 3
Starting a script with #! /path/to/bash -option
, or equivalently running
bash -option /path/to/script
, has the same effect as inserting set -option
inside the script just after its #!
line.
set -x
is essentially just for debugging; you wouldn't want it left on normally.
The effect of set -e
is a lot more complicated than the manual suggests. The more precise description is roughly:
After invoking
set -e
, if an untested command exits with a non-zero status, it will cause the shell to exit with that exit status.
I say more precise because it's still pretty vague: the list of situations where the command is considered tested is arcane and difficult to predict. The exact rules differ between shells and even change between versions of the same shell.
set -e
does not cause Bash to exit in response to non-zero exit status of any commands:
- between
if
/elif
andthen
; or - between
while
/until
anddo
; or - following
!
; or - followed by
||
or&&
or&
(though for&
the correspondingwait
orfg
may fail); or - followed by
|
, unlessset -o pipefail
is in effect; or - inside
$( ... )
when used as part of a command (not bare assignments and/or redirections, which means thatfoo=$( bar )
will fail ifbar
fails, butlocal foo=$( bar )
will not); or - within a compound command where the above apply; or
- inside a shell function invoked from anywhere the above apply.
The effects on compound commands and shell functions are recursive.
Because the shell itself exits with a non-zero status, the effect can propagate out of a subshell.
The !
exemption applies even if the inverted exit status is ignored.
Conversely, set -e
triggers on every other non-zero status, regardless of whether it means "fail" or simply "false". For example ((x++))
will have a non-zero "fail" exit status when x
is initially 0.
Because it's such a mess, there's a school of thought that says set -e
should be avoided in favour of explicitly checking all commands.
Or if you still want to use set -e
, remember to write !
in front of any ((arithmetic))
groups.
Related videos on Youtube
coneybeare
Updated on September 17, 2022Comments
-
coneybeare 10 months
I have some bash scripts I have setup that mostly use
#!/bin/bash
but I regularly come across some that look like
#!/bin/bash -e #!/bin/bash -x #!/bin/bash -ex
and so on.
Can someone explain the meaning and benefits of these shebang options and whether they apply to other shebangs?
-
Dennis Williamson over 12 yearsThose options are specific to Bash (or other interpreter). They may be the same for other shells (dash and ksh, for example), but they would be different for other interpreters such as AWK and Python. You can use many of the options that the interpreter accepts. The options are interpreter-specific while the shebang is a kernel feature.
-
-
Julian over 12 years+1 And
-ex
does both -
Caoilte almost 11 yearsIt's confusing because they look like command-line options passed into Bash.
-
cYrus almost 11 years@Caoilte: And indeed they are (from
man bash
):In addition to the single-character shell options documented in the description of the set builtin command, bash interprets the following options when it is invoked: [...]
. -
slhck over 4 yearsI do this all the time, but I wouldn't say it's “more portable” — in fact, the risk of having the user run something completely different (from what you can assume a system ships with) is much greater. For example, Ubuntu still ships Bash 4, while a user may choose to run Bash 5.
-
smac89 almost 4 yearsYea the
env
usage is not good especially for scripts that run python because you simply do not know if the defaultpython
is version 2 or 3 and that makes a world of difference for scripts that need a specific version. Better to be explicit than crafty -
Martin Kealey about 1 yearRequiring
$PATH
to be set by the user is a recipe for trouble. For a script that's part of a package, it's preferable for for the package maintainer to set the#!
to suit the class of system that it will be installed onto, or failing that, for it to be detected during installation. Otherwise it's preferable for the administrator to set the#!
line when installing the script so that their users don't need to know or care. Of course, if the instructions originally just said “Runbash scriptname
" instead of "Run./scriptname
" that would also solve the problem.