The Bash logo
I’m working on a hobby book project, updating a previously self-published book to format it in LaTeX. The book includes lots of charts and graphs. I decided to format the charts using gnuplot so it’s easy to keep everything looking the same.
There’s a neat “terminal” type in gnuplot that lets me create charts in PDF format but use LaTeX to control the text; that’s an ideal solution, because it means the chart labels and titles will all look consistent with the rest of the book’s interior. And I can include the charts with a LaTeX command like \include{fig1-1.tex} which automatically includes the PDF version of the chart; that makes things pretty straightforward.
However, the book has a lot of charts. I don’t look forward to having a ton of gnuplot files sitting in my main book project directory. To keep things tidy, I thought I’d put the charts in one or more subdirectories. But once I put the charts into a subdirectory, referencing the charts with \include stopped working; for example, my fig1-1.tex file was trying to include a PDF chart image that was in the subdirectory, so my main document couldn’t find it. When I processed the book, with the charts in a subdirectory, LaTeX gave this error:
! LaTeX Error: File `fig1-1' not found.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.123 ...th={360.00bp},height={216.00bp}]{fig1-1}}
The solution was to write a short Bash script to automate editing every generated LaTeX file in a subdirectory, so my LaTeX files reference the chart image from that subdirectory. That fixes my book project.
Writing a short Bash script is a great way to automate a common task. Here’s what I wrote:
How my files are organized
To include a chart in my LaTeX book, I add a figure like this:
\begin{figure}[ht]
\centering
\include{figs/fig1-1.tex}
\caption{Sample chart}
\end{figure}
This does several things, but I don’t need to go into too much detail to explain it. At a high level, it creates a figure, centers everything, includes the figs/fig1-1.tex file to reference the actual chart, and appends a caption.
My book project currently has a single directory called figs for figures and charts, but I might split that up later so that figures from chapter 1 go into a figs1 directory, figures from chapter 2 go into figs2, and so on.
In each directory, I have files that allow gnuplot to generate the charts: for example, one chart might be called lab.in that reads data from lab.dat to generate a chart. The lab.in file contains a line that set the “terminal” type to cairolatex to generate the PDF chart and the LaTeX file to include it, and another line to save the output to a LaTeX file. The rest of the file contains gnuplot commands to generate the chart:
set terminal cairolatex pdf blacktext mono
set output 'fig1-1.tex'
f(x)=i + s*x
fit f(x) 'lab.dat' via i,s
set xlabel '$t^2 / 2$'
set ylabel '$y$'
plot 'lab.dat' notitle, f(x) notitle
When I process the lab.in file with gnuplot, I get a LaTeX file named fig1-1.tex that references a PDF chart image as fig1-1.pdf.
Every time my LaTeX book includes a chart, it uses \include to reference the LaTeX file generated by gnuplot; that included file references the actual chart image as a PDF, using \includegraphics, and adds the chart labels using LaTeX text.
Fixing the files
My challenge is that I need to edit all of the LaTeX files in the figs directory, so the \includegraphics command references a PDF image in the subdirectory. This allows my book to correctly locate and insert the chart image.
I wrote this short Bash script, which I’ve saved as subfix.sh:
#!/bin/bash
# usage: subfix.sh {dir}
# fix LaTeX input files so they can be properly included from a subdirectory
if [ $# -ne 1 ] ; then
echo "usage: $(basename $0) {dir}"
exit 1
fi
for file in $(cd $1 ; ls *.tex); do
f=${file%.tex}
echo $1/$file
sed -i -e "s!{$f}!{$1/$f}!" $1/$file
done
There are two important sections here; let’s go through them one at a time.
The “if” block at the top evaluates how many command line arguments the script was provided. If the script was not given any command line arguments, $# returns zero. For one argument, $# returns 1. And so on for other command line arguments.
In this case, my script only expects one command line argument: the name of a subdirectory. If $# is not exactly 1, the script exits.
The “for” block loops through every LaTeX file in the subdirectory. We can use $1 to reference the script’s first command line argument, which we know is the name of a subdirectory. For each file, the script saves the name in the f variable, without the .tex extension, and prints it (helpful for debugging). Then the script uses the sed command to edit the LaTeX file in place using the -i option.
The sed command uses s to replace text in the file. In this case, it looks for the file’s “base” name (without the extension) inside curly braces. There should only show up one time in each LaTeX file, but sed will look for any lines that match, and change the text to add the subdirectory.
The script in action
This will make more sense if we look at an example. In my figs directory, I used gnuplot to generate a chart, and save the output to figs1-1.tex. Because I used pdfcairo for the “terminal” type, gnuplot also generated figs1-1.pdf with the chart image. The figs1-1.tex file references the PDF:
$ ls -1 figs/fig*
figs/fig1-1.pdf
figs/fig1-1.tex
$ grep fig1-1 figs/fig1-1.tex
\put(0,0){\includegraphics[width={360.00bp},height={216.00bp}]{fig1-1}}%
If I run the subfix.sh script from the “main” directory in my book project, and give it the figs directory, it will update all of the LaTeX files in that directory, including the figs1-1.tex file, to add the subdirectory name.
$ ./subfix.sh figs
figs/fig1-1.tex
$ grep fig1-1 figs/fig1-1.tex
\put(0,0){\includegraphics[width={360.00bp},height={216.00bp}]{figs/fig1-1}}%
And now when my book project “includes” the figs/fig1-1.tex file to insert the chart, the book will correctly find the fig1-1.pdf file in the figs subdirectory.
Automating fixes with Bash
Writing short Bash scripts like this can help automate all kinds of tasks. Right now, my book has all of the figures in a single figs directory. If I have several dozen charts in that subdirectory, my Bash script will fix them all—at the same time.