Bash #1 - Execution Relay
Posted April 25, 2021 ‐ 3 min read
The 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
incarnation 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.
Relaying execution
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 flock
,
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.
|
Execution result:
$ 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$@
- same"$*"
- relay all arguments as one argument withIFS
separator 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 exec
prefix.
Full example
#!/bin/bash
mkdir -p ~/.locker || exit -1
exec flock ~/.locker/my-lock-file "$@"
|