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 {} $< $< > $@