bash shell is somewhat like the lingua-franca of the UNIX-based shell
scripting world, as nothing else manages to displace it. The usefulness of a
language is significant if it is omnipresent.
In this post I begin a series about programming in
bash. Its earlier
sh is less of a concern to me because
bash can be found in
almost in every place where
sh is present.
The pitfalls in
bash-programming for the initiate or non-frequent shell
script programmer are numerous. However, they are easy to avoid by just
sticking in to some rules.
Occasionally you want to relay an execution of one program to another. Possible reasons: providing arguments, environment variable hacks, path, and so forth.
For example, let's create a mutex around another program using
a program to manage locks from shell scripts.
flock has the following syntax:
flock [options] file|directory command [arguments]
From this, it is apparent that we would need to relay arguments to the executed program.
Relaying arguments (always use
It is important to understand the difference between the following expressions:
Let's test this with a simple
bash script. The script executes Python to see
what list of strings was actually relayed as an argument list.
$ bash test.sh foo bar 'hello world' \? ['foo', 'bar', 'hello', 'world', '1', '2', 'm'] ['foo', 'bar', 'hello', 'world', '1', '2', 'm'] ['foo bar hello world ?'] ['foo', 'bar', 'hello world', '?']
From this we can see there's only one good option. Let's break it down to what happens:
$*- don't quote arguments, perform wildcard expansion and then relay
"$*"- relay all arguments as one argument with
IFSseparator concatenation (space if undefined), don't perform wildcard expansion
"$@"- relay arguments and quote them, don't expand, equivalent to
"$1" "$2" "$3" ...
There is more information about this in bash's docs.
Avoiding a redundant shell process
Once we reach executing the wrapper program, the shell process has done its
job. Therefore, it would be best to replace it with the wrapped program. This
can be done using an
#!/bin/bash mkdir -p ~/.locker || exit -1 exec flock ~/.locker/my-lock-file "$@"