string formatting with files in bash
The problem
I need to generate a file with very simple structure, just a LaTeX file with a few include
statements.
Until today, I was copying a template and manually changing it.
This was slow, redundant, and error prone.
It makes more sense to just generate the files from the template instead.
I wanted to use bash for this rather than a scripting language, because then it will connect better with the whole GNU Make workflow of the project.
The solution
I started by putting the template in the Makefile and using echo
, as I normally do.
This gave me some problems, so I started looking into alternatives that would let me keep the template as a separate file.
Most people recommend printf
as a more robust alternative to echo
.
The problem was, how to pass printf
the FORMAT
argument from a file?
Use xargs
.
Here’s a simple example.
First you’ll need a format.txt
file, or whatever you choose to call it.
$ printf "%%s and %%s\n" > format.txt
The %%
just escapes the special character %
for printf
, so the contents of template.txt
are %s and %s
.
$ cat format.txt
%s and %s
We can use it as follows:
$ cat format.txt | xargs -0 -I{} printf {} A B
A and B
We read this command as printf <contents of format.txt> A B
.
xargs
always makes me do some mental gymnastics.
The -0
flag prevents xargs
from messing with the actual contents, i.e. removing whitespace.
-I{}
allows me to take the text and pass it as the first argument with printf {} ...
.
Otherwise it would be the last argument.
Here’s how I actually use it in my Makefile:
%.tex: tex/%.tex texformat.txt preamble.tex
cat texformat.txt | xargs -0 -I{} printf {} $< $< > $@