On 19 Aug 2013, Randal L. Schwartz posted this shell script, which was intended to ensure, on Linux, "that only one instance of [the] script is running, without race conditions or having to clean up lock files":
#!/bin/sh # randal_l_schwartz_001.sh ( if ! flock -n -x 0 then echo "$$ cannot get flock" exit 0 fi echo "$$ start" sleep 10 # for testing. put the real task here echo "$$ end" ) < $0
It seems to work as advertised:
$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh  11863 11863 start 11864 cannot get flock $ 11863 end + Done ./randal_l_schwartz_001.sh $
Here is what I do understand:
- The script redirects (
<) a copy of its own contents (i.e. from
$0) to the STDIN (i.e. file descriptor
0) of a subshell.
- Within the subshell, the script attempts to get a non-blocking, exclusive lock (
flock -n -x) on file descriptor
- If that attempt fails, the subshell exits (and so does the main script, as there is nothing else for it to do).
- If the attempt instead succeeds, the subshell runs the desired task.
Here are my questions:
- Why does the script need to redirect, to a file descriptor inherited by the subshell, a copy of its own contents rather than, say, the contents of some other file? (I tried redirecting from a different file and re-running as above, and the execution order changed: the non-backgrounded task gained the lock before the background one. So, maybe using the file's own contents avoids race conditions; but how?)
- Why does the script need to redirect, to a file descriptor inherited by the subshell, a copy of a file's contents, anyway?
- Why does holding an exclusive lock on file descriptor
0in one shell prevent a copy of the same script, running in a different shell, from getting an exclusive lock on file descriptor
0? Don't shells have their own, separate copies of the standard file descriptors (
2, i.e. STDIN, STDOUT, and STDERR)?