I have a handful of files containing simple configuration in which I need to modify values. There are some useful constraints that simplify my implementation:
- The file consists of single lines of the form
key=value(no multi-line values). - Key names are written using letters, digits and underscore only (no regex meta-characters).
- Keys are always at start of line, although the
=may have spaces on one or both sides. - All meaningful keys are already present in the file; we never need to add any lines.
- The values I'm writing never contain backslash.
This is the function I wrote to make these changes:
# change_values FILE [KEY VALUE]...
# Edit FILE, replacing each KEY
# with corresponding VALUE.
change_values()
{
file=${1?change_values requires a file name}; shift
sed -f <(printf '/^%s *=/s/=.*/= %s/\n' "${@//\//\\/}") \
-i "$file"
}
To demonstrate its operation, I used this simple test:
set -eu
d=$(mktemp -d)
trap 'rm -r "$d"' EXIT
cd "$d"
cat >config <<END
# This is a comment
tmpdir=/var/tmp
command = stat
alt_command = rm
# Spaces around = are ignored
format= %c%d
format2 =%s%d
END
change_values config \
tmpdir '/tmp/workdir' \
format "%d%s" \
command 'ls' \
nonexistent 'no_effect'
cmp config - <<END
# This is a comment
tmpdir= /tmp/workdir
command = ls
alt_command = rm
# Spaces around = are ignored
format= %d%s
format2 =%s%d
END
scommand is crystal clear and the test is lovely, thank you, looks good, especially the "no comments!"^caret anchor.shifting the filename out of$@made perfect sense. I imagine that${@//\//\\/}does the Right Thing, but as expressed it is obscure, and if double backwhack is going to be so troublesome perhaps this isn't the appropriate tool for the job. I would neither delegate nor accept maintenance tasks on this code. \$\endgroup\$