{"id":14548,"date":"2026-08-28T01:00:00","date_gmt":"2026-08-28T05:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=14548"},"modified":"2026-08-14T12:51:50","modified_gmt":"2026-08-14T16:51:50","slug":"automating-file-edits-with-bash","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=14548","title":{"rendered":"Automating file edits with Bash"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"14548\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div>\n<p class=\"wp-block-paragraph\">I&#8217;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 <a href=\"https:\/\/www.both.org\/?p=14522\">using gnuplot<\/a> so it&#8217;s easy to keep everything looking the same.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s a neat &#8220;terminal&#8221; type in gnuplot that lets me create charts in PDF format but use LaTeX to control the text; that&#8217;s an ideal solution, because it means the chart labels and titles will all look consistent with the rest of the book&#8217;s interior. And I can include the charts with a LaTeX command like <code>\\include{fig1-1.tex}<\/code> which automatically includes the PDF version of the chart; that makes things pretty straightforward.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, the book has a <em>lot<\/em> of charts. I don&#8217;t look forward to having a ton of gnuplot files sitting in my main book project directory. To keep things tidy, I thought I&#8217;d put the charts in one or more subdirectories. But once I put the charts into a subdirectory, referencing the charts with <code>\\include<\/code> stopped working; for example, my <code>fig1-1.tex<\/code> file was trying to include a PDF chart image that was in the <em>subdirectory<\/em>, so my main document couldn&#8217;t find it. When I processed the book, with the charts in a subdirectory, LaTeX gave this error:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>! LaTeX Error: File `fig1-1' not found.\n\nSee the LaTeX manual or LaTeX Companion for explanation.\nType  H &lt;return&gt;  for immediate help.\n ...\n\nl.123 ...th={360.00bp},height={216.00bp}]{fig1-1}}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Writing a short Bash script is a great way to automate a common task. Here&#8217;s what I wrote:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How my files are organized<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To include a chart in my LaTeX book, I add a figure like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\begin{figure}&#91;ht]\n\\centering\n\\include{figs\/fig1-1.tex}\n\\caption{Sample chart}\n\\end{figure}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This does several things, but I don&#8217;t need to go into too much detail to explain it. At a high level, it creates a figure, centers everything, includes the <code>figs\/fig1-1.tex<\/code> file to reference the actual chart, and appends a caption.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My book project currently has a single directory called <code>figs<\/code> for figures and charts, but I might split that up later so that figures from chapter 1 go into a <code>figs1<\/code> directory, figures from chapter 2 go into <code>figs2<\/code>, and so on.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In each directory, I have files that allow gnuplot to generate the charts: for example, one chart might be called <code>lab.in<\/code> that reads data from <code>lab.dat<\/code> to generate a chart. The <code>lab.in<\/code> file contains a line that set the &#8220;terminal&#8221; type to <code>cairolatex<\/code> 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:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>set terminal cairolatex pdf blacktext mono\nset output 'fig1-1.tex'\n\nf(x)=i + s*x\nfit f(x) 'lab.dat' via i,s\nset xlabel '$t^2 \/ 2$'\nset ylabel '$y$'\nplot 'lab.dat' notitle, f(x) notitle<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When I process the <code>lab.in<\/code> file with gnuplot, I get a LaTeX file named <code>fig1-1.tex<\/code> that references a PDF chart image as <code>fig1-1.pdf<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every time my LaTeX book includes a chart, it uses <code>\\include<\/code> to reference the LaTeX file generated by gnuplot; that <em>included<\/em> file references the actual chart image as a PDF, using <code>\\includegraphics<\/code>, and adds the chart labels using LaTeX text.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fixing the files<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">My challenge is that I need to edit all of the LaTeX files in the <code>figs<\/code> directory, so the <code>\\includegraphics<\/code> command references a PDF image in the subdirectory. This allows my book to correctly locate and insert the chart image.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I wrote this short Bash script, which I&#8217;ve saved as <code>subfix.sh<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# usage: subfix.sh {dir}\n# fix LaTeX input files so they can be properly included from a subdirectory\n\nif &#91; $# -ne 1 ] ; then\n    echo \"usage: $(basename $0) {dir}\"\n    exit 1\nfi\n\nfor file in $(cd $1 ; ls *.tex); do\n    f=${file%.tex}\n    echo $1\/$file\n    sed -i -e \"s!{$f}!{$1\/$f}!\" $1\/$file\ndone<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There are two important sections here; let&#8217;s go through them one at a time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The &#8220;if&#8221; block at the top evaluates how many command line arguments the script was provided. If the script was not given any command line arguments, <code>$#<\/code> returns zero. For one argument, <code>$#<\/code> returns 1. And so on for other command line arguments.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this case, my script only expects one command line argument: the name of a subdirectory. If <code>$#<\/code> is not exactly 1, the script exits.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The &#8220;for&#8221; block loops through every LaTeX file in the subdirectory. We can use <code>$1<\/code> to reference the script&#8217;s first command line argument, which we know is the name of a subdirectory. For each file, the script saves the name in the <code>f<\/code> variable, <em>without<\/em> the <code>.tex<\/code> extension, and prints it (helpful for debugging). Then the script uses the <strong>sed<\/strong> command to edit the LaTeX file <em>in place<\/em> using the <code>-i<\/code> option.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <strong>sed<\/strong> command uses <code>s<\/code> to replace text in the file. In this case, it looks for the file&#8217;s &#8220;base&#8221; name (without the extension) inside curly braces. There should only show up one time in each LaTeX file, but <strong>sed<\/strong> will look for any lines that match, and change the text to add the subdirectory.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The script in action<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This will make more sense if we look at an example. In my <code>figs<\/code> directory, I used gnuplot to generate a chart, and save the output to <code>figs1-1.tex<\/code>. Because I used <code>pdfcairo<\/code> for the &#8220;terminal&#8221; type, gnuplot also generated <code>figs1-1.pdf<\/code> with the chart image. The <code>figs1-1.tex<\/code> file references the PDF:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ ls -1 figs\/fig*\nfigs\/fig1-1.pdf\nfigs\/fig1-1.tex\n\n$ grep fig1-1 figs\/fig1-1.tex\n    \\put(0,0){\\includegraphics&#91;width={360.00bp},height={216.00bp}]{fig1-1}}%<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If I run the <code>subfix.sh<\/code> script from the &#8220;main&#8221; directory in my book project, and give it the <code>figs<\/code> directory, it will update all of the LaTeX files in that directory, including the <code>figs1-1.tex<\/code> file, to add the subdirectory name.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ .\/subfix.sh figs\nfigs\/fig1-1.tex\n\n$ grep fig1-1 figs\/fig1-1.tex\n    \\put(0,0){\\includegraphics&#91;width={360.00bp},height={216.00bp}]{figs\/fig1-1}}%<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And now when my book project &#8220;includes&#8221; the <code>figs\/fig1-1.tex<\/code> file to insert the chart, the book will correctly find the <code>fig1-1.pdf<\/code> file in the <code>figs<\/code> subdirectory.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Automating fixes with Bash<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code>figs<\/code> directory. If I have several dozen charts in that subdirectory, my Bash script will fix them all\u2014at the same time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s okay to write a short Bash script to automate a task<\/p>\n","protected":false},"author":33,"featured_media":4314,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":"","_members_access_role":[],"_members_access_error":""},"categories":[149,100],"tags":[151,104],"class_list":["post-14548","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bash","category-command-line","tag-bash","tag-command-line"],"modified_by":"Jim Hall","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14548","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=14548"}],"version-history":[{"count":2,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14548\/revisions"}],"predecessor-version":[{"id":14551,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/14548\/revisions\/14551"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/4314"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=14548"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=14548"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=14548"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}