<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>.:: Marcos Dione/StyXman's glob ::. (Posts about bash)</title><link>https://www.grulic.org.ar/~mdione/glob/</link><description></description><atom:link href="https://www.grulic.org.ar/~mdione/glob/categories/bash.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2025 &lt;a href="mailto:mdione@grulic.org.ar"&gt;Marcos Dione&lt;/a&gt; </copyright><lastBuildDate>Thu, 29 May 2025 15:41:12 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Logging shell scripts' executions: a better way</title><link>https://www.grulic.org.ar/~mdione/glob/posts/logging-shell-scripts-executions-a-better-way/</link><dc:creator>Marcos Dione</dc:creator><description>&lt;p&gt;Some 11 years ago &lt;a href="https://www.grulic.org.ar/~mdione/glob/posts/logging-shell-scripts-executions/"&gt;I wrote&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nx"&gt;wanna&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;at&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;least&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tracing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;its&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Well&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="nx"&gt;simply&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;surround&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+%&lt;/span&gt;&lt;span class="nx"&gt;F&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Well, a couple of years back at my $OLD_JOB we needed to do exactly that, even with the option turn it on and off at
will. For the latter, we decided to use an envvar. Here's how we did it, it's much more robust:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEBUG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PS4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt; $(date +"%Y-%m_dT%H:%M:%S") &amp;gt; $(basename "${BASH_SOURCE}") &amp;gt; ${FUNCNAME[0]:=__main__}():${LINENO} &amp;gt; '&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/var/log/foo.log
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;BASH_TRACEFD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-x
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There's a lot there, so let's try to unpack it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The last line, &lt;code&gt;set -x&lt;/code&gt;, makes &lt;code&gt;bash&lt;/code&gt; print every line executed&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PS4&lt;/code&gt; defines the "prompt" for such lines. We're doing a lot here, so:&lt;/li&gt;
&lt;li&gt;We start printing a date. Maybe the Y-m-s is too much, but it looks familiar from other log files. This is command
    substitution that is executed every time a line is printed, the same way you can do fun stuff with your shell prompt.&lt;/li&gt;
&lt;li&gt;We print the filename of the source file.&lt;/li&gt;
&lt;li&gt;The the function from the top of the stack, or &lt;code&gt;__main__&lt;/code&gt; in the case of the main body of the script. Guess who wrote
    this and what's their favorite language :)&lt;/li&gt;
&lt;li&gt;Then the line number&lt;/li&gt;
&lt;li&gt;Then &lt;code&gt;bash&lt;/code&gt; prints the executed line&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bash&lt;/code&gt; has grown organically, which is a nice way to say 'hacky'. This incantation is  how you open a file for
  appending and copy the file descriptor to the file descriptor 5. Remember 0 is stdin, 1 is stdout and 2 is stderr.
  We're playing safe with 5.&lt;/li&gt;
&lt;li&gt;We tell &lt;code&gt;bash&lt;/code&gt; to use fd 5 for printing all this, sending it to the file. By default stderr is used, which you
  probably want to keep for your own script.&lt;/li&gt;
&lt;li&gt;All this only if &lt;code&gt;DEBUG&lt;/code&gt; has any contents. We usually do &lt;code&gt;export DEBUG=1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Enjoy.&lt;/p&gt;
&lt;h2&gt;Update&lt;/h2&gt;
&lt;p&gt;Forgot to mention: this will slow down your execution time noticeably, specially if it's a 65k LoC beast. Ask me how I
know, but only after buying me a beer :)&lt;/p&gt;</description><category>bash</category><category>debug</category><guid>https://www.grulic.org.ar/~mdione/glob/posts/logging-shell-scripts-executions-a-better-way/</guid><pubDate>Fri, 01 Dec 2023 23:17:04 GMT</pubDate></item></channel></rss>