{"id":4346,"date":"2024-03-15T04:00:00","date_gmt":"2024-03-15T08:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=4346"},"modified":"2024-03-15T06:06:24","modified_gmt":"2024-03-15T10:06:24","slug":"using-if-in-a-bash-script","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=4346","title":{"rendered":"Using &#8216;if&#8217; in a Bash script"},"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=\"4346\" 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>Automation is the key to everything. If I have a two-step process, I&#8217;m going to automate it. If it&#8217;s a multi-step process, where the next step depends on the successful execution of the one before it, you can bet I&#8217;m going to automate it.<\/p>\n\n\n\n<p>In a recent example, I wanted to view several Simplified Docbook files to see what the output looks like. Doing this involves an XSLT transformation and requires installing several dependencies, which seems a bit heavy when all I want to do is view the output as a PDF. Instead, I created a simple Bash script to process my Simplified Docbook file using tools that come installed by default on my system.<\/p>\n\n\n\n<p>This is a three-step conversion: verify the file with <code>xmllint<\/code>, convert the file with <code>pandoc<\/code>, then convert to PDF with LibreOffice. Each step requires that the previous step in the chain runs successfully. This is an excellent example of how to use the <code>if<\/code> statement in Bash.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Verifying the file with xmllint<\/h2>\n\n\n\n<p>Since I&#8217;m editing the Docbook file by hand, I want to be sure that my file is correct. The process for this is validation using a DTD file. I can use the <code>xmllint<\/code> program to verify my Simplified Docbook articles using a copy of the DTD that I already saved on my system.<\/p>\n\n\n\n<p><code>xmllint<\/code> can take several command line options, but I&#8217;m most interested in the <code>--dtdvalid<\/code> option that takes a DTD file to verify against. To verify a single Simplified Docbook file, I can use this <code>xmllint<\/code> command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>xmllint --dtdvalid $HOME\/lib\/docbook\/sdocbook.dtd article.docbook<\/code><\/pre>\n\n\n\n<p>If the file contains any errors, <code>xmllint<\/code> prints a message for each instance and exits with a non-zero status. If the file doesn&#8217;t contain any errors, <code>xmllint<\/code> prints a clean version of the XML file. You can optionally suppress that output with <code>--noout<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Converting the file with pandoc<\/h2>\n\n\n\n<p>An easy way to process Simplified Docbook files is with the <code>pandoc<\/code> program, which can read from and write to a long list of file types, including Docbook. However, to generate a PDF, <code>pandoc<\/code> requires LaTeX, which I don&#8217;t have installed. But if all I want is to view the output of my Simplified Docbook article, I can use <code>pandoc<\/code> to convert my file into a word processor format like LibreOffice ODT:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pandoc --from=docbook --to=odt article.docbook -o article.odt<\/code><\/pre>\n\n\n\n<p>If <code>pandoc<\/code> successfully generates the output file without issues, it returns with a zero exit status. For any errors, <code>pandoc<\/code> will return a specific nonzero code that indicates what went wrong.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conditional execution with &#8216;if&#8217;<\/h2>\n\n\n\n<p>It&#8217;s important to know that the Simplified Docbook file is correct. If the file contains errors, <code>pandoc<\/code> usually prints an error; but for small discrepancies, <code>pandoc<\/code> might silently try to do the best it can. That doesn&#8217;t give me a good indication of what my article should look like.<\/p>\n\n\n\n<p>So it&#8217;s important to only convert files that pass the <code>xmllint<\/code> test. I can do that using a short Bash script that relies on the <code>if<\/code> statement to perform conditional execution. The most basic format looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if <em>command<\/em> ; then <em>commands<\/em> ; fi<\/code><\/pre>\n\n\n\n<p>Since the <code>xmllint<\/code> command exits with a zero status on success, we can use that as the test in the <code>if<\/code> statement. My Bash script looks like this, where <code>\"$1\"<\/code> is the first file listed when I run the script:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\nif xmllint --noout --dtdvalid $HOME\/lib\/docbook\/sdocbook.dtd \"$1\" ; then\n&nbsp;&nbsp;pandoc --from=docbook --to=odt \"$1\" -o out.odt\nfi<\/code><\/pre>\n\n\n\n<p>If successful, this generates a LibreOffice file called <code>out.odt<\/code> with the results of my Simplified Docbook article.<\/p>\n\n\n\n<p>I prefer to write this in a longer form that first runs the command, then uses <code>if<\/code> with the <code>$?<\/code> Bash variable, which returns the exit code of the previous command. When using <code>if<\/code> this way, use the <code>test<\/code> command, implemented internally in Bash using <code>[<\/code> as a shorthand. For example, to run the <code>xmllint<\/code> command and test if the previous exit code was zero, then run the <code>pandoc<\/code> command, use this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\nxmllint --noout --dtdvalid $HOME\/lib\/docbook\/sdocbook.dtd \"$1\"\n\nif &#91; $? -eq 0 ] ; then\n  pandoc --from=docbook --to=odt \"$1\" -o out.odt\nfi<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Converting to PDF with LibreOffice<\/h2>\n\n\n\n<p>If I want to take another step and convert the LibreOffice file into a PDF file, I can use LibreOffice from the command line using the <code>--convert-to<\/code> option. To convert a single file called <code>out.odt<\/code> into a PDF file, you can run LibreOffice this way:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>libreoffice --convert-to pdf out.odt<\/code><\/pre>\n\n\n\n<p>Let&#8217;s add this command to the Bash script, after doing the initial conversion using <code>pandoc<\/code>. We can use another <code>if<\/code> statement and the <code>$?<\/code> variable to perform a second test like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\nxmllint --noout --dtdvalid $HOME\/lib\/docbook\/sdocbook.dtd \"$1\"\n\nif &#91; $? -eq 0 ] ; then\n  pandoc --from=docbook --to=odt \"$1\" -o out.odt\n\n  if &#91; $? -eq 0 ] ; then\n    libreoffice --convert-to pdf out.odt\n  fi\nfi<\/code><\/pre>\n\n\n\n<p>First, the script runs <code>xmllint<\/code> to test the file. If that is successful, the script uses <code>pandoc<\/code> to convert the file into LibreOffice ODT format. And if that conversion runs without errors, the script runs LibreOffice from the command line to convert the ODT file into PDF. Notice that the <code>if<\/code> statements are nested inside each other.<\/p>\n\n\n\n<p>If successful, this generates a new file called <code>out.pdf<\/code> with the results of my Simplified Docbook article in PDF format.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using Bash to automate everything<\/h2>\n\n\n\n<p>You can improve this Bash script with a little extra intelligence. For example, you might need to process more than one Simplified Docbook file. Instead of running the script multiple times, you could add a <code>for<\/code> loop inside the Bash script to process all the files, one after the other. The general format of a Bash loop looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for <em>variable<\/em> in <em>list<\/em> ; do <em>commands<\/em> ; done<\/code><\/pre>\n\n\n\n<p>To loop through a list of files, we can add a <code>for<\/code> loop that processes all the files on the command line. Bash provides a few ways to get all the command line arguments to a script; using <code>\"$@\"<\/code> will preserve any spaces in filenames. So you can iterate over a list of files like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\nfor file in \"$@\" ; do\n  echo $file\ndone<\/code><\/pre>\n\n\n\n<p>As a final step, let&#8217;s integrate the <code>for<\/code> loop into our script to process Simplified Docbook source files into PDF:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\nfor file in \"$@\" ; do\n  xmllint --noout --dtdvalid $HOME\/lib\/docbook\/sdocbook.dtd \"$1\"\n\n  tmpfile=${file%.docbook}.odt\n\n  if &#91; $? -eq 0 ] ; then\n    pandoc --from=docbook --to=odt \"$1\" -o $tmpfile\n\n    if &#91; $? -eq 0 ] ; then\n      libreoffice --convert-to pdf $tmpfile\n    fi\n  fi\ndone<\/code><\/pre>\n\n\n\n<p>This uses an extra feature in Bash that can pull apart a variable using parameters. The <code>${file%.docbook}<\/code> expansion returns the value of the file variable then <em>removes<\/em> the last occurrence of <code>.docbook<\/code> in the name. For example, if <code>file<\/code> had the value <code>article.docbook<\/code>, the <code>${file%.docbook}<\/code> expansion would give just <code>article<\/code>. The script uses this to assign a new <code>.odt<\/code> file extension with this line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>tmpfile=${file%.docbook}.odt<\/code><\/pre>\n\n\n\n<p>If I save my script as <code>sdocbook<\/code> and make it executable with <code>chmod +x sdocbook<\/code>, I can run the script to easily convert a list of Simplified Docbook files into PDF format. The output for each will be the base filename with a <code>.pdf<\/code> extension, so <code>article.docbook<\/code> will be converted to <code>article.pdf<\/code>.<\/p>\n\n\n\n<p>Writing a short Bash script can save typing lots of instructions at the command line. This sample Bash script collects three programs in one script, but only runs the next command if the previous program ran successfully. It&#8217;s a great way to automate and simplify the command line.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Writing a short Bash script can save typing lots of instructions at the command line.<\/p>\n","protected":false},"author":33,"featured_media":4314,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[149,100,80],"tags":[151,104,91],"class_list":["post-4346","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bash","category-command-line","category-tips-and-tricks","tag-bash","tag-command-line","tag-linux"],"modified_by":"David Both","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/4346","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=4346"}],"version-history":[{"count":4,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/4346\/revisions"}],"predecessor-version":[{"id":4414,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/4346\/revisions\/4414"}],"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=4346"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4346"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4346"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}