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

How would I pass multiple commands to flock as in the example above?

As far as I understand, flock takes different flags (-x for exclusive, -w for timeout), then the file to lock, and then the command to run. I'm not sure how I would pass two commands into this function (set variable with locked file's contents, and then increment this file).

My goal here is to create a somewhat atomic increment for a file by locking it each time a script tries to access the counter.txt file.

6

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')
  • awesome, thanks so much! The explanation you provided definitely saved me from another hour of headaches – d-_-b Dec 29 '13 at 20:17
4

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
eval exec "$counterfd<&-"

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

2

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.