Issue
#!/bin/bash
if [ ! -f numbers ]; then echo 0 > numbers; fi
count=0
touch numbers
echo $count > numbers
while [[ $count != 100 ]]; do
if ln numbers numbers.lock
then
count=`expr $count + 1`
n=`tail -1 numbers`
expr $n + 1 >> numbers
rm numbers.lock
fi
done
What can I do to avoid the race condition of count=`expr $count + 1`
and the n=`tail -1 numbers`
, so that when I run two of the scripts at the same time, it only goes to 100, and not 200. I've researched on multiple websites, but there are no concise answers without making a huge function.
Solution
You are already safely avoiding the actual race condition with the lock file. The problem you are describing can be avoided two ways.
(1) Move the lock file outside the main loop, so that two instances of your program cannot run their main loop at the same time. If one is running, the other will have to wait until it's done, then start replacing the output file.
#!/bin/bash
# FIXME: broken, see comments
while true; do
if ! ln numbers numbers.lock
then
sleep 1
else
if [ ! -f numbers ]; then echo 0 > numbers; fi
count=0
touch numbers
#echo $count > numbers # needless, isn't it?
while [[ $count != 100 ]]; do
count=`expr $count + 1`
n=`tail -1 numbers`
expr $n + 1 >> numbers
rm numbers.lock
done
break
fi
done
(2) Make the two instances cooperate, by examining what the contents of the file are. In other words, force them to stop looping when the number reaches 100, regardless of how many other processes are writing to this file. (I guess there is an iffy corner case when there are more than 100 instances running.)
#!/bin/bash
# FIXME: should properly lock here, too
if [ ! -f numbers ]; then echo 0 > numbers; fi
n=0
touch numbers
while [[ $n -lt 100 ]]; do
if ln numbers numbers.lock
then
n=$(expr $(tail -1 numbers) + 1 | tee numbers)
rm numbers.lock
fi
done
Depending on your requirements, you might actually want the script to clobber any previous value in the file when a new instance of the script is starting, but if not, the echo 0 > numbers
should be governed by the lock file, too.
You really want to avoid expr
in a Bash script; Bash has built-in arithmetic operators. I have not attempted to refactor that part here, but you probably should. Perhaps prefer Awk so you can factor out the tail
too; awk '{ i=$0 } END { print 1+i }' numbers
Answered By - tripleee