About Simplicity
"Simple" and "short" are not the same thing. echo $foo
is shorter than echo "$foo"
, but it actually does far more things: It splits the value of foo
apart on characters in IFS
, evaluates each result of that split as a glob expression, and then recombines them.
Similarly, making your code simpler -- as in, limiting the number of steps in the process it goes through -- is not at all the same thing as making it shorter.
Incrementing One Piece, Leaving Others Unmodified
if IFS=. read -r major rest <version.txt || [ -n "$major" ]; then
echo "$((major + 1)).$rest" >"version.txt.$$" && mv "version.txt.$$" version.txt
else
echo "ERROR: Unable to read version number from version.txt" >&2
exit 1
fi
Incrementing Major Version, Discarding Others
if IFS=. read -r major rest <version.txt || [ -n "$major" ]; then
echo "$((major + 1))" >"version.txt.$$" && mv "version.txt.$$" "version.txt"
else
echo "ERROR: Unable to read version number from version.txt" >&2
exit 1
fi
Rationale
Both of the above are POSIX-compliant, and avoid relying on any capabilities not built into the shell.
IFS=. read -r first second third <input
reads the first line of input, and splits it on .
s into the shell variables first
, second
and third
; notably, the third
column in this example includes everything after the first two, so if you had a.b.c.d.e.f
, you would get first=a; second=b; third=d.e.f
-- hence the name rest
to make this clear. See BashFAQ #1 for a detailed explanation.
$(( ... ))
creates an arithmetic context in all POSIX-compliant shells. It's only useful for integer math, but since we split the pieces out with the read
, we only need integer math. See http://wiki.bash-hackers.org/syntax/arith_expr
- Writing to
version.txt.$$
and renaming if that write is successful prevents version.txt
from being left empty or corrupt if a failure takes place between the open and the write. (A version that was worried about symlink attacks would use mktemp
, instead of relying on $$
to generate a unique tempfile name).
- Proceeding through to the write only if the
read
succeeds or [ -n "$major" ]
is true prevents the code from resetting the version to 1
(by adding 1 to an empty string, which evaluates in an arithmetic context as 0) if the read fails.