You could do the reading value, incrementing, writing new value with awk
, and argument handling in sh
:
#! /bin/sh -
file=/path/to/counter
case "$#:$1" in
(1:in) incr=1;;
(1:out) incr=-1;;
(*) echo >&2 'need "in" or "out" as the one and only one argument'
exit 1
esac
awk -v incr="$incr" '
{print $0 + incr > FILENAME; exit}
END {if (!NR) print incr > FILENAME}' 3<> "$file" "$file"
That expects $file
contains one line with one number¹ and increments or decrements it. If the line doesn't contain a number or the file is empty, it will be treated as 0. That's better than using shell arithmetic evaluation to do the increment as those are not safe if the input is not guaranteed to have sane values.
If the file didn't exist beforehand, it will be created (via 3<> "$file"
) and considered as if it contained 0 as well.
Note that that script is not safe against concurrent execution as two instances running at the same time could read the same old value and increase it independantly. To address that, if your system has the flock
command, you could replace the awk
command with:
{
flock 3 &&
awk -v incr="$incr" '
{print $0 + incr > FILENAME; exit}
END {if (!NR) print incr > FILENAME}' "$file"
} 3<> "$file"
Where flock
obtains an exclusive lock on the file.
¹ the number can be integer or floating point (even potentially expressed in hexadecimal with some awk implementation and in some environment), but note that non-integer numbers are expressed with 6 digits of precision, so 99999.5
would become 100000
and 999999.5
would become 1e+06
after increment. So if dealing with floating point numbers and high values, you may want to add a -v OFMT='%.15g'
for instance to increase the precision.