Pass multiple commands to flock

Invoke a shell explicitly.

flock -x -w 5 ~/counter.txt sh -c 'COUNTER=$(cat counter.txt); echo $((COUNTER + 1)) > ~/counter.txt'

Note that any variable that you change is local to that shell instance. For example, the COUNTER variable will not be updated in the calling script: you'll have to read it back from the file (but it may have changed in the meantime), or as the output of the command:

new_counter=$(flock -x -w 5 ~/counter.txt sh -c 'COUNTER=$(cat counter.txt); echo $((COUNTER + 1)) | tee ~/counter.txt')

Or you can flock a file descriptor

exec {counterfd}<~/counter.txt
flock -x -w 5 "$counterfd"
COUNTER=$(cat ~/counter.txt)
COUNTER=$(( COUNTER +1 ))
echo "$COUNTER" >~/counter.txt
exec {counterfd}<&-

This also has the benefit of allowing you to use the counter variable directly, unlike subshell based approaches.


The flock tool is a little tricky to use and the man page is pretty short. The man page provides three ways to use the tool:

  • flock [options] <file|directory> <command> [command args]
  • flock [options] <file|directory> -c <command>
  • flock [options] <file descriptor number>

The way this question is worded I would definitely use the third form of flock. If you go further down in the man page for flock there are some examples which show the exact syntax for using the third form:

#!/bin/bash
(
 flock -n 9 || exit 1
 echo "commands executed under lock..."
 echo "go here..."
) 9>/tmp/mylockfile

I added the #!/bin/bash.

I have successfully used this form of flock.