<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>command line on foosel.net</title><link>https://foosel.net/tags/command-line/</link><description>Recent content in command line on foosel.net</description><generator>Hugo</generator><language>en-us</language><copyright>Gina Häußge (foosel)</copyright><lastBuildDate>Tue, 12 Mar 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://foosel.net/tags/command-line/feed.xml" rel="self" type="application/rss+xml"/><item><title>How to quickly create a header modifying reverse proxy with mitmproxy</title><link>https://foosel.net/til/2024-03-12-how-to-quickly-create-a-header-modifying-reverse-proxy-with-mitmproxy/</link><pubDate>Tue, 12 Mar 2024 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2024-03-12-how-to-quickly-create-a-header-modifying-reverse-proxy-with-mitmproxy/</guid><description>&lt;p&gt;I&amp;rsquo;m currently in the process of testing some changes on OctoPrint involving its automatic user login via request headers, and
for that needed to quickly set up a reverse proxy that would modify the headers of the requests going to the development server
for some quick testing.&lt;/p&gt;
&lt;p&gt;Specifically, I wanted a quick CLI tool that would allow me to set up a reverse proxy listening on port 5555, forwarding to
&lt;code&gt;http://localhost:5000&lt;/code&gt; while also setting the headers &lt;code&gt;X-Remote-User&lt;/code&gt; to &lt;code&gt;remote&lt;/code&gt; and &lt;code&gt;X-Remote-Host&lt;/code&gt; to &lt;code&gt;localhost:5555&lt;/code&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;m currently in the process of testing some changes on OctoPrint involving its automatic user login via request headers, and
for that needed to quickly set up a reverse proxy that would modify the headers of the requests going to the development server
for some quick testing.</p>
<p>Specifically, I wanted a quick CLI tool that would allow me to set up a reverse proxy listening on port 5555, forwarding to
<code>http://localhost:5000</code> while also setting the headers <code>X-Remote-User</code> to <code>remote</code> and <code>X-Remote-Host</code> to <code>localhost:5555</code>.</p>
<p>Enter <a href="https://mitmproxy.org/"><code>mitmproxy</code></a>, or more specifically its <code>mitmdump</code> tool, which turned out to be a great tool for this job.</p>
<p>All I needed was to run the following command:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mitmdump --mode reverse:http://localhost:5000@5555 --modify-headers <span style="color:#e6db74">&#34;/X-Remote-User/remote&#34;</span> --modify-headers <span style="color:#e6db74">&#34;/X-Forwarded-Host/localhost:5555&#34;</span>
</span></span></code></pre></div><p>This does the following:</p>
<ul>
<li><code>--mode reverse:http://localhost:5000@5555</code> sets up a reverse proxy listening on port 5555, forwarding to <code>http://localhost:5000</code></li>
<li><code>--modify-headers &quot;/X-Remote-User/remote&quot;</code> sets the <code>X-Remote-User</code> header to <code>remote</code></li>
<li><code>--modify-headers &quot;/X-Forwarded-Host/localhost:5555&quot;</code> sets the <code>X-Forwarded-Host</code> header to <code>localhost:5555</code></li>
</ul>
<p>With that the <a href="https://community.octoprint.org/t/reverse-proxy-configuration-examples/1107">reverse proxy test page in OctoPrint</a>
turned all green and I could test my changes without having to set up an actual reverse proxy in front of the development server.</p>
]]></content:encoded></item><item><title>How to print Deutsche Post stamps via the command line on a Brother QL label printer</title><link>https://foosel.net/til/2024-01-11-how-to-print-deutsche-post-stamps-via-the-command-line-on-a-brother-ql-label-printer/</link><pubDate>Thu, 11 Jan 2024 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2024-01-11-how-to-print-deutsche-post-stamps-via-the-command-line-on-a-brother-ql-label-printer/</guid><description>&lt;p&gt;&lt;em&gt;Update from 2024-01-12: I&amp;rsquo;ve updated the scripts to support both 50mm and 62mm wide labels, and added some more whitespace trimming to the basic stamp. The post has been adjusted accordingly.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I recently acquired a &lt;a href="https://www.brother-usa.com/products/QL820NWB"&gt;Brother QL-820NWB label printer&lt;/a&gt; to be able to quickly create labels for boxes and such, and ideally also print out Deutsche Post&amp;rsquo;s &amp;ldquo;print yourself&amp;rdquo; stamps with it. The Deutsche Post stamp shop allows me to download PDFs targeting the 62mm wide endless labels for that printer, for the two types of stamps I&amp;rsquo;m interested in (stamp, and address label with stamp). But my attempts in printing those directly to the printer through Gnome&amp;rsquo;s printer integration weren&amp;rsquo;t successful, things were too small, the cutter didn&amp;rsquo;t work etc.&lt;/p&gt;</description><content:encoded><![CDATA[<p><em>Update from 2024-01-12: I&rsquo;ve updated the scripts to support both 50mm and 62mm wide labels, and added some more whitespace trimming to the basic stamp. The post has been adjusted accordingly.</em></p>
<p>I recently acquired a <a href="https://www.brother-usa.com/products/QL820NWB">Brother QL-820NWB label printer</a> to be able to quickly create labels for boxes and such, and ideally also print out Deutsche Post&rsquo;s &ldquo;print yourself&rdquo; stamps with it. The Deutsche Post stamp shop allows me to download PDFs targeting the 62mm wide endless labels for that printer, for the two types of stamps I&rsquo;m interested in (stamp, and address label with stamp). But my attempts in printing those directly to the printer through Gnome&rsquo;s printer integration weren&rsquo;t successful, things were too small, the cutter didn&rsquo;t work etc.</p>
<p>I knew that printing to the printer via my local instance of <a href="https://github.com/pklaus/brother_ql_web">brother_ql_web</a> works flawlessly, and that the library this is based on, <a href="https://github.com/pklaus/brother_ql">brother_ql</a>, has a command line interface. So I thought, why not just convert the PDFs to individual PNGs, and then print those?</p>
<p>Through the magic of some shell scripting, I&rsquo;m now able to do just that, right from the command line.</p>
<p>I installed the <code>brother_ql</code> Python package via pip:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>pip install --user brother_ql
</span></span></code></pre></div><p>I had to do a little manual patch to make it work with the latest versions of the required Pillow dependency, by editing <code>brother_ql/conversion.py</code> and changing <code>Image.ANTIALIAS</code> to <code>Image.LANCZOS</code>.</p>
<p>I also made sure my <code>.bash_profile</code> contains the address and model of my printer:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export BROTHER_QL_PRINTER<span style="color:#f92672">=</span>tcp://192.168.x.x
</span></span><span style="display:flex;"><span>export BROTHER_QL_MODEL<span style="color:#f92672">=</span>QL-820NWB
</span></span></code></pre></div><p>Then I created two shell scripts, one for printing stamps and one for printing labels.</p>
<p>The first one, <code>porto_print</code>, takes care of printing the stamps. It resizes, trims, adds a new border and then extends to the native width for the selected label size (50mm by default, or 62mm if requested) while keeping a right alignment:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Usage:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#   porto_print &lt;pdf&gt; [50|62]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>tmpdir<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>mktemp -d<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>cleanup<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>  rm -rf <span style="color:#e6db74">&#34;</span>$tmpdir<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>trap cleanup EXIT
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>PDF<span style="color:#f92672">=</span>$1
</span></span><span style="display:flex;"><span>LABEL<span style="color:#f92672">=</span><span style="color:#e6db74">${</span>2<span style="color:#66d9ef">:-</span>50<span style="color:#e6db74">}</span>
</span></span><span style="display:flex;"><span>PNG<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>basename <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>PDF%.*<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">case</span> $LABEL in
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;50&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    WIDTH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;554&#34;</span>
</span></span><span style="display:flex;"><span>    ;;
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;62&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    WIDTH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;696&#34;</span>
</span></span><span style="display:flex;"><span>    ;;
</span></span><span style="display:flex;"><span>  *<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    echo <span style="color:#e6db74">&#34;Unsupported label size: </span>$LABEL<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>    exit -1
</span></span><span style="display:flex;"><span>    ;;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">esac</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Converting PDF to individual PNGs...&#34;</span>
</span></span><span style="display:flex;"><span>pdftoppm <span style="color:#e6db74">&#34;</span>$PDF<span style="color:#e6db74">&#34;</span> <span style="color:#e6db74">&#34;</span>$tmpdir<span style="color:#e6db74">/</span>$PNG<span style="color:#e6db74">&#34;</span> -png -r <span style="color:#ae81ff">600</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> file in <span style="color:#66d9ef">$(</span>ls $tmpdir/*.png<span style="color:#66d9ef">)</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;Printing </span>$file<span style="color:#e6db74">...&#34;</span>
</span></span><span style="display:flex;"><span>  mogrify -background white -bordercolor white -resize 696x -trim -border 25x25 -gravity east -extent <span style="color:#e6db74">${</span>WIDTH<span style="color:#e6db74">}</span>x284 <span style="color:#e6db74">&#34;</span>$file<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>  brother_ql print -l $LABEL <span style="color:#e6db74">&#34;</span>$file<span style="color:#e6db74">&#34;</span> 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><p>The second one, <code>porto_address_print</code>, does basically the same, just with slightly different parameters and left alignment:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Usage:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#   porto_address_print &lt;pdf&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>tmpdir<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>mktemp -d<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>cleanup<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>  rm -rf <span style="color:#e6db74">&#34;</span>$tmpdir<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>trap cleanup EXIT
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>PDF<span style="color:#f92672">=</span>$1
</span></span><span style="display:flex;"><span>LABEL<span style="color:#f92672">=</span><span style="color:#e6db74">${</span>2<span style="color:#66d9ef">:-</span>50<span style="color:#e6db74">}</span>
</span></span><span style="display:flex;"><span>PNG<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>basename <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>PDF%.*<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">case</span> $LABEL in
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;50&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    WIDTH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;554&#34;</span>
</span></span><span style="display:flex;"><span>    ;;
</span></span><span style="display:flex;"><span>  <span style="color:#e6db74">&#34;62&#34;</span><span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    WIDTH<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;696&#34;</span>
</span></span><span style="display:flex;"><span>    ;;
</span></span><span style="display:flex;"><span>  *<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>    echo <span style="color:#e6db74">&#34;Unsupported label size: </span>$LABEL<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>    exit -1
</span></span><span style="display:flex;"><span>    ;;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">esac</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;Converting PDF to individual PNGs...&#34;</span>
</span></span><span style="display:flex;"><span>pdftoppm <span style="color:#e6db74">&#34;</span>$PDF<span style="color:#e6db74">&#34;</span> <span style="color:#e6db74">&#34;</span>$tmpdir<span style="color:#e6db74">/</span>$PNG<span style="color:#e6db74">&#34;</span> -png -r <span style="color:#ae81ff">600</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> file in <span style="color:#66d9ef">$(</span>ls $tmpdir/*.png<span style="color:#66d9ef">)</span>; <span style="color:#66d9ef">do</span>
</span></span><span style="display:flex;"><span>  echo <span style="color:#e6db74">&#34;Printing </span>$file<span style="color:#e6db74">...&#34;</span>
</span></span><span style="display:flex;"><span>  mogrify -bordercolor white -background white -resize 696x -trim -border 25x25 -gravity West -extent <span style="color:#e6db74">${</span>WIDTH<span style="color:#e6db74">}</span>x839 <span style="color:#e6db74">&#34;</span>$file<span style="color:#e6db74">&#34;</span> 
</span></span><span style="display:flex;"><span>  brother_ql print -l $LABEL <span style="color:#e6db74">&#34;</span>$file<span style="color:#e6db74">&#34;</span> 
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">done</span>
</span></span></code></pre></div><p>Both of these were placed under <code>~/.local/bin</code> and made executable. I can now call them both from anywhere on the command line, just passing the path to the PDF to print.</p>
<p>The result is one or more nicely printed stamps or address labels, ready to be stuck to an envelope:</p>
<p><img alt="Printed stamp and printed stamp with address label, freshly printed from example files through the two scripts" loading="lazy" src="/til/2024-01-11-how-to-print-deutsche-post-stamps-via-the-command-line-on-a-brother-ql-label-printer/result.jpg"></p>
<p>Now, this should hopefully make it easier for me to print all those address labels for OctoPrint sticker shipments in the future ;) Next step: Automated QR code labels for the various boxes on my shelves ^^</p>
]]></content:encoded></item><item><title>How to trim screenshots via the commandline</title><link>https://foosel.net/til/2023-02-09-how-to-trim-screenshots-via-the-commandline/</link><pubDate>Thu, 09 Feb 2023 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2023-02-09-how-to-trim-screenshots-via-the-commandline/</guid><description>&lt;p&gt;I just had to trim a bunch of screenshots that had some black borders around them. I didn&amp;rsquo;t want to do this manually or via a GUI, but ideally batch-able via the commandline. Thankfully, that&amp;rsquo;s one of the many things that &lt;a href="https://imagemagick.org/"&gt;ImageMagick&lt;/a&gt; can do for you.&lt;/p&gt;
&lt;p&gt;I put all my screenshot PNGs into a folder, and then in that folder ran this &lt;a href="https://imagemagick.org/script/mogrify.php"&gt;&lt;code&gt;mogrify&lt;/code&gt;&lt;/a&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;magick mogrify -trim -define trim:percent-background&lt;span style="color:#f92672"&gt;=&lt;/span&gt;0% -background black -path output/ *.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I found that I had to add the &lt;code&gt;-define trim:percent-background=0%&lt;/code&gt; option to get rid of &lt;em&gt;all&lt;/em&gt; black borders, as otherwise on some of the images a very slim one ended up remaining. I also specified the background color with &lt;code&gt;-background black&lt;/code&gt; to make sure that it really only trimmed the black borders.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I just had to trim a bunch of screenshots that had some black borders around them. I didn&rsquo;t want to do this manually or via a GUI, but ideally batch-able via the commandline. Thankfully, that&rsquo;s one of the many things that <a href="https://imagemagick.org/">ImageMagick</a> can do for you.</p>
<p>I put all my screenshot PNGs into a folder, and then in that folder ran this <a href="https://imagemagick.org/script/mogrify.php"><code>mogrify</code></a> command:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>magick mogrify -trim -define trim:percent-background<span style="color:#f92672">=</span>0% -background black -path output/ *.png
</span></span></code></pre></div><p>I found that I had to add the <code>-define trim:percent-background=0%</code> option to get rid of <em>all</em> black borders, as otherwise on some of the images a very slim one ended up remaining. I also specified the background color with <code>-background black</code> to make sure that it really only trimmed the black borders.</p>
<p>I then could combine the resulting images into a PDF with <a href="https://pypi.org/project/img2pdf/">img2pdf</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>img2pdf --output output.pdf *.png
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Look, a hidden bonus TIL!&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>How to quickly generate a QR Code with transparent background</title><link>https://foosel.net/til/2023-02-06-how-to-quickly-generate-a-qr-code-with-transparent-background/</link><pubDate>Mon, 06 Feb 2023 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2023-02-06-how-to-quickly-generate-a-qr-code-with-transparent-background/</guid><description>&lt;p&gt;For an upcoming presentation I wanted to quickly generate a QR Code of my web site&amp;rsquo;s URL to include on the final slide. Since my slide theme has a green gradient background with white text, I wanted the QR Code to be white on a transparent background, as a PNG.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href="https://github.com/soldair/node-qrcode"&gt;node-qrcode&lt;/a&gt; which runs easily via &lt;code&gt;npx&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx qrcode -o output.png -d FFFF -l &lt;span style="color:#ae81ff"&gt;0000&lt;/span&gt; -w &lt;span style="color:#ae81ff"&gt;500&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;https://foosel.net&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-o output.png&lt;/code&gt; sets the output file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-d FFFF&lt;/code&gt; sets the dark color (usually black) to white with 100% opacity&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-l 0000&lt;/code&gt; sets the light color (usually white) to black with 0% opacity - fully transparent&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-w 500&lt;/code&gt; sets the size to 500px&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For further options like error correction or QR code version, or how to use it as a library or in the browser, see node-qrcode&amp;rsquo;s repo linked above.&lt;/p&gt;</description><content:encoded><![CDATA[<p>For an upcoming presentation I wanted to quickly generate a QR Code of my web site&rsquo;s URL to include on the final slide. Since my slide theme has a green gradient background with white text, I wanted the QR Code to be white on a transparent background, as a PNG.</p>
<p>Enter <a href="https://github.com/soldair/node-qrcode">node-qrcode</a> which runs easily via <code>npx</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npx qrcode -o output.png -d FFFF -l <span style="color:#ae81ff">0000</span> -w <span style="color:#ae81ff">500</span>  <span style="color:#e6db74">&#34;https://foosel.net&#34;</span>
</span></span></code></pre></div><ul>
<li><code>-o output.png</code> sets the output file</li>
<li><code>-d FFFF</code> sets the dark color (usually black) to white with 100% opacity</li>
<li><code>-l 0000</code> sets the light color (usually white) to black with 0% opacity - fully transparent</li>
<li><code>-w 500</code> sets the size to 500px</li>
</ul>
<p>For further options like error correction or QR code version, or how to use it as a library or in the browser, see node-qrcode&rsquo;s repo linked above.</p>
<p>I for one am happy with the result of this little exercise:</p>
<p><img alt="The final slide of a presentation. It says &ldquo;Thank you for you attention!&rdquo;. Below that I&rsquo;ve listed my Mastodon account @foosel@chaos.social, my GitHub account @foosel and my website&rsquo;s address foosel.net. A big white QR Code is placed right underneath, a handdrawn arrow points from website to code." loading="lazy" src="/til/2023-02-06-how-to-quickly-generate-a-qr-code-with-transparent-background/slide.png"></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Yes, there are also online services that can do this for you, no, I didn&rsquo;t want to look through dozens of them trying to find one that didn&rsquo;t attempt to make me subscribe to something just to change the color of the generated QR code. Local CLI ftw.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>How to use jq to extract new posts from a JSON Feed</title><link>https://foosel.net/til/2023-02-02-how-to-use-jq-to-extract-new-posts-from-a-json-feed/</link><pubDate>Thu, 02 Feb 2023 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2023-02-02-how-to-use-jq-to-extract-new-posts-from-a-json-feed/</guid><description>&lt;p&gt;I&amp;rsquo;m currently looking into ways to automate some stuff around new posts on this page (be it blog or TIL post) directly during the page build on GitHub Actions. For this, I first need to be able to reliably &lt;em&gt;detect&lt;/em&gt; new posts, from a bash run step. So here&amp;rsquo;s how to do that with &lt;a href="https://stedolan.github.io/jq/"&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The idea is to get the current &lt;a href="https://foosel.net/til/how-to-add-json-feed-support-to-hugo/"&gt;&lt;code&gt;feed.json&lt;/code&gt;&lt;/a&gt; prior to publishing the page, and then compare it to the one that was just generated during the build. If there are any differences, we know that there are new posts and can trigger further actions from there.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;m currently looking into ways to automate some stuff around new posts on this page (be it blog or TIL post) directly during the page build on GitHub Actions. For this, I first need to be able to reliably <em>detect</em> new posts, from a bash run step. So here&rsquo;s how to do that with <a href="https://stedolan.github.io/jq/"><code>jq</code></a>.</p>
<p>The idea is to get the current <a href="/til/how-to-add-json-feed-support-to-hugo/"><code>feed.json</code></a> prior to publishing the page, and then compare it to the one that was just generated during the build. If there are any differences, we know that there are new posts and can trigger further actions from there.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Get current feed.json</span>
</span></span><span style="display:flex;"><span>curl -s https://foosel.net/til/feed.json &gt; feed.current.json
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Get new feed.json</span>
</span></span><span style="display:flex;"><span>cp public/til/feed.json feed.next.json
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Compare the two, this is where the magic happens</span>
</span></span><span style="display:flex;"><span>jq --slurpfile current til.current.json --slurpfile next til.next.json -n <span style="color:#e6db74">&#39;$next[0].items - $current[0].items&#39;</span> &gt; til.json
</span></span></code></pre></div><p>Let&rsquo;s go through this <code>jq</code> command there:</p>
<ul>
<li><code>--slurpfile &lt;variable&gt; &lt;file&gt;</code> reads in the given files and makes it accessible as an array contained in the given variable. In this case we read in <code>til.current.json</code> and make it accessible as <code>$current</code>, and also read in <code>til.next.json</code> and make it accessible as <code>$next</code>.</li>
<li><code>-n</code> doesn&rsquo;t wait for input on stdin.</li>
<li><code>'$next[0].items - $current[0].items'</code> subtracts the items from the new feed from the items in the current feed<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</li>
<li><code>&gt; til.json</code> writes the output to <code>til.json</code>.</li>
</ul>
<p><code>til.json</code> will then contain all new items (as long as there weren&rsquo;t more than the feed&rsquo;s item size), can be uploaded as an artifact and then used in further jobs<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The indexing (e.g. <code>$new[0]</code>) here is needed due to <code>--slurpfile</code> creating an array from the read file. I admittedly need to experiment more with this option to fully understand it, but for the purpose here it works.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>My current goal is to move my announcements on Mastodon for new posts from my NodeRED install got the page build, and also send any webmentions for links contained in new posts as well.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>How to grep a log for multiline errors</title><link>https://foosel.net/til/2023-02-01-how-to-grep-a-log-for-multline-errors/</link><pubDate>Wed, 01 Feb 2023 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2023-02-01-how-to-grep-a-log-for-multline-errors/</guid><description>&lt;p&gt;I just found myself in the position to have to &lt;code&gt;grep&lt;/code&gt; an OctoPrint log file for error log entries with attached Python stack traces. I wanted to not only get the starting line where the exception log output starts, but the full stack trace up until the next regular log line.&lt;/p&gt;
&lt;p&gt;The format of the lines in &lt;code&gt;octoprint.log&lt;/code&gt; is a simple &lt;code&gt;%(asctime)s - %(name)s - %(levelname)s - %(message)s&lt;/code&gt;, so a log with an error and attached exception looks like this:&lt;/p&gt;</description><content:encoded><![CDATA[<p>I just found myself in the position to have to <code>grep</code> an OctoPrint log file for error log entries with attached Python stack traces. I wanted to not only get the starting line where the exception log output starts, but the full stack trace up until the next regular log line.</p>
<p>The format of the lines in <code>octoprint.log</code> is a simple <code>%(asctime)s - %(name)s - %(levelname)s - %(message)s</code>, so a log with an error and attached exception looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>2023-01-30 17:50:45,704 - octoprint.events.fire - DEBUG - Firing event: Disconnecting (Payload: None)
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,704 - octoprint.events - DEBUG - Sending action to &lt;bound method PrinterStateConnection._onEvent of &lt;octoprint.server.util.sockjs.PrinterStateConnection object at 0x000001635CB6EE50&gt;&gt;
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,705 - octoprint.plugin - DEBUG - Calling on_event on action_command_notification
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,705 - octoprint.plugin - DEBUG - Calling on_event on action_command_prompt
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,705 - octoprint.plugin - DEBUG - Calling on_event on announcements
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,705 - octoprint.plugin - DEBUG - Calling on_event on file_check
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,706 - octoprint.plugin - DEBUG - Calling on_event on firmware_check
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,706 - octoprint.plugin - DEBUG - Calling on_event on pluginmanager
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,706 - octoprint.plugin - DEBUG - Calling on_event on softwareupdate
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,706 - octoprint.plugin - DEBUG - Calling on_event on tracking
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,711 - octoprint.plugin - DEBUG - Calling on_event on mqtt
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,732 - octoprint.events.fire - DEBUG - Firing event: Disconnected (Payload: None)
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,735 - octoprint.events - DEBUG - Sending action to &lt;function Server.run.&lt;locals&gt;.&lt;lambda&gt; at 0x000001635BDB2CA0&gt;
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,750 - octoprint.events - ERROR - Got an exception while sending event Disconnected (Payload: None) to &lt;function Server.run.&lt;locals&gt;.&lt;lambda&gt; at 0x000001635BDB2CA0&gt;
</span></span><span style="display:flex;"><span>Traceback (most recent call last):
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\events.py&#34;, line 197, in _work
</span></span><span style="display:flex;"><span>    listener(event, payload)
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\server\__init__.py&#34;, line 1212, in &lt;lambda&gt;
</span></span><span style="display:flex;"><span>    octoprint.events.Events.DISCONNECTED, lambda e, p: run_autorefresh()
</span></span><span style="display:flex;"><span>                                                       ^^^^^^^^^^^^^^^^^
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\server\__init__.py&#34;, line 1195, in run_autorefresh
</span></span><span style="display:flex;"><span>    autorefresh.stop()
</span></span><span style="display:flex;"><span>    ^^^^^^^^^^^^^^^^
</span></span><span style="display:flex;"><span>AttributeError: &#39;RepeatedTimer&#39; object has no attribute &#39;stop&#39;
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,753 - octoprint.events - DEBUG - Sending action to &lt;bound method PrinterStateConnection._onEvent of &lt;octoprint.server.util.sockjs.PrinterStateConnection object at 0x000001635CB6EE50&gt;&gt;
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,755 - octoprint.plugin - DEBUG - Calling on_event on action_command_notification
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,756 - octoprint.server.util.sockjs - DEBUG - Socket message held back until permissions cleared, added to backlog: plugin
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,758 - octoprint.plugins.action_command_notification - INFO - Notifications cleared
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,758 - octoprint.plugin - DEBUG - Calling on_event on action_command_prompt
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,758 - octoprint.plugin - DEBUG - Calling on_event on announcements
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,759 - octoprint.plugin - DEBUG - Calling on_event on file_check
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,759 - octoprint.plugin - DEBUG - Calling on_event on firmware_check
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,759 - octoprint.server.util.sockjs - DEBUG - Socket message held back until permissions cleared, added to backlog: plugin
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,763 - octoprint.plugin - DEBUG - Calling on_event on pluginmanager
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,764 - octoprint.plugin - DEBUG - Calling on_event on softwareupdate
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,764 - octoprint.plugin - DEBUG - Calling on_event on tracking
</span></span></code></pre></div><p>What I now wanted is for <code>grep</code> to spit out just the <code>ERROR</code> line and the attached stack trace:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>2023-01-30 17:50:45,750 - octoprint.events - ERROR - Got an exception while sending event Disconnected (Payload: None) to &lt;function Server.run.&lt;locals&gt;.&lt;lambda&gt; at 0x000001635BDB2CA0&gt;
</span></span><span style="display:flex;"><span>Traceback (most recent call last):
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\events.py&#34;, line 197, in _work
</span></span><span style="display:flex;"><span>    listener(event, payload)
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\server\__init__.py&#34;, line 1212, in &lt;lambda&gt;
</span></span><span style="display:flex;"><span>    octoprint.events.Events.DISCONNECTED, lambda e, p: run_autorefresh()
</span></span><span style="display:flex;"><span>                                                       ^^^^^^^^^^^^^^^^^
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\server\__init__.py&#34;, line 1195, in run_autorefresh
</span></span><span style="display:flex;"><span>    autorefresh.stop()
</span></span><span style="display:flex;"><span>    ^^^^^^^^^^^^^^^^
</span></span><span style="display:flex;"><span>AttributeError: &#39;RepeatedTimer&#39; object has no attribute &#39;stop&#39;
</span></span></code></pre></div><p>For this I needed a way to set <code>grep</code> to match multiple lines and do a (non-matching) look ahead for the end. It turns out that the secret to success here is to treat the whole input as one line, use Perl compatible regex mode, and make sure to set the multiline flag. After some fiddling around on <a href="https://regex101.com/r/qYOrnT/1">regex101.com</a> and reading up on <a href="https://perldoc.perl.org/perlre#Extended-Patterns">Perl&rsquo;s regex options</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, I came up with the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>grep -Pazo &#39;(?m)^\N+\- ERROR \-\N*\n(^\N*?\n)*?(?=\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3} \- )&#39; octoprint.log
</span></span></code></pre></div><p>Let&rsquo;s walk through this:</p>
<ul>
<li><code>-P</code> enables Perl compatible regex mode</li>
<li><code>-a</code> enables text mode</li>
<li><code>-z</code> turns all newlines into null bytes and thus treats the whole input as a single line for finding matches<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li><code>-o</code> only outputs the matched part of the line (otherwise we&rsquo;d get the whole file printed out)</li>
<li><code>(?m)</code> enables multiline mode</li>
<li><code>^\N+\- ERROR \-\N*\n</code> matches the first line of the error, which is the one that starts with the timestamp and package and contains the word <code>ERROR</code></li>
<li><code>(^\N*?\n)*?</code> non-greedily matches all following lines of the error, which are anything but a newline followed by a newline</li>
<li><code>(?=\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3} \- )</code> is a positive look-ahead that matches a line starting with a timestamp again, which signifies the end of the error&rsquo;s lines</li>
</ul>
<p>Hooray, it works 🥳:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-plain" data-lang="plain"><span style="display:flex;"><span>❯ grep -Pazo &#39;(?m)^\N+\- ERROR \-\N*\n(^\N*?\n)*?(?=\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3} \- )&#39; octoprint.log
</span></span><span style="display:flex;"><span>2023-01-30 17:50:45,750 - octoprint.events - ERROR - Got an exception while sending event Disconnected (Payload: None) to &lt;function Server.run.&lt;locals&gt;.&lt;lambda&gt; at 0x000001635BDB2CA0&gt;
</span></span><span style="display:flex;"><span>Traceback (most recent call last):
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\events.py&#34;, line 197, in _work
</span></span><span style="display:flex;"><span>    listener(event, payload)
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\server\__init__.py&#34;, line 1212, in &lt;lambda&gt;
</span></span><span style="display:flex;"><span>    octoprint.events.Events.DISCONNECTED, lambda e, p: run_autorefresh()
</span></span><span style="display:flex;"><span>                                                       ^^^^^^^^^^^^^^^^^
</span></span><span style="display:flex;"><span>  File &#34;C:\Devel\OctoPrint\OctoPrint\src\octoprint\server\__init__.py&#34;, line 1195, in run_autorefresh
</span></span><span style="display:flex;"><span>    autorefresh.stop()
</span></span><span style="display:flex;"><span>    ^^^^^^^^^^^^^^^^
</span></span><span style="display:flex;"><span>AttributeError: &#39;RepeatedTimer&#39; object has no attribute &#39;stop&#39;
</span></span></code></pre></div><p>(And yes, I&rsquo;ve fixed the error that lead to this stack trace as well 😉)</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>I don&rsquo;t know about you, but I always forget about positive/negative look-ahead/behind and pattern-match modifiers.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The downside of this is that now <code>-n</code> (print line number of match) will not work anymore and just happily report line 1 for every single match.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>How to detect Termux in a script</title><link>https://foosel.net/til/2023-01-23-how-to-detect-termux-in-a-script/</link><pubDate>Mon, 23 Jan 2023 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2023-01-23-how-to-detect-termux-in-a-script/</guid><description>&lt;p&gt;If you need to detect whether you are running in Termux from a bash script, check if &lt;code&gt;$PREFIX&lt;/code&gt; contains the string &lt;code&gt;com.termux&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo $PREFIX | grep -o &lt;span style="color:#e6db74"&gt;&amp;#34;com.termux&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This can also be used to set a variable in a &lt;a href="https://taskfile.dev"&gt;Taskfile&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;vars&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;TERMUX&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;{{and .PREFIX (contains &amp;#34;com.termux&amp;#34; .PREFIX)}}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://www.reddit.com/r/termux/comments/co46qw/how_to_detect_in_a_bash_script_that_im_in_termux/"&gt;Source&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you need to detect whether you are running in Termux from a bash script, check if <code>$PREFIX</code> contains the string <code>com.termux</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>echo $PREFIX | grep -o <span style="color:#e6db74">&#34;com.termux&#34;</span>
</span></span></code></pre></div><p>This can also be used to set a variable in a <a href="https://taskfile.dev">Taskfile</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">vars</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">TERMUX</span>: <span style="color:#e6db74">&#39;{{and .PREFIX (contains &#34;com.termux&#34; .PREFIX)}}&#39;</span>
</span></span></code></pre></div><p><a href="https://www.reddit.com/r/termux/comments/co46qw/how_to_detect_in_a_bash_script_that_im_in_termux/">Source</a></p>
]]></content:encoded></item><item><title>How to determine an RPi kernel version and build without booting it</title><link>https://foosel.net/til/2022-06-16-how-to-determine-an-rpi-kernel-version-and-build-without-booting-it/</link><pubDate>Thu, 16 Jun 2022 00:00:00 +0000</pubDate><guid>https://foosel.net/til/2022-06-16-how-to-determine-an-rpi-kernel-version-and-build-without-booting-it/</guid><description>&lt;p&gt;To figure out the kernel version and build without booting it, e.g. to install matching device drivers during an automated image build in something like &lt;a href="https://github.com/OctoPrint/CustoPiZer"&gt;CustoPiZer&lt;/a&gt;, use something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; version_and_build_for_kernelimg&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kernelimg&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# uncompressed kernel?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;strings $kernelimg | grep &lt;span style="color:#e6db74"&gt;&amp;#39;Linux version&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; echo&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; -z &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$output&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# compressed kernel, needs more work, see https://raspberrypi.stackexchange.com/a/108107&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pos&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;LC_ALL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;C grep -P -a -b -m &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; --only-matching &lt;span style="color:#e6db74"&gt;&amp;#39;\x1f\x8b\x08&amp;#39;&lt;/span&gt; $kernelimg | cut -f &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; -d :&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dd &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$kernelimg of&lt;span style="color:#f92672"&gt;=&lt;/span&gt;kernel.gz skip&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$pos iflag&lt;span style="color:#f92672"&gt;=&lt;/span&gt;skip_bytes
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;gzip --decompress --stdout kernel.gz | strings | grep &lt;span style="color:#e6db74"&gt;&amp;#39;Linux version&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; echo&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo $output | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $3}&amp;#39;&lt;/span&gt; | tr -d &lt;span style="color:#e6db74"&gt;&amp;#39;+&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; build&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo $output | awk -F&lt;span style="color:#e6db74"&gt;&amp;#34;#&amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{print $NF}&amp;#39;&lt;/span&gt; | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$version&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$build&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Version: &lt;/span&gt;$kernel&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Build: &lt;/span&gt;$build&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Cannot determine kernel version and build number for &lt;/span&gt;$kernelimg&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this has only been tested with kernels on RaspberryPi OS images, YMMV.&lt;/p&gt;</description><content:encoded><![CDATA[<p>To figure out the kernel version and build without booting it, e.g. to install matching device drivers during an automated image build in something like <a href="https://github.com/OctoPrint/CustoPiZer">CustoPiZer</a>, use something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#66d9ef">function</span> version_and_build_for_kernelimg<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>    kernelimg<span style="color:#f92672">=</span>$1
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># uncompressed kernel?</span>
</span></span><span style="display:flex;"><span>    output<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>strings $kernelimg | grep <span style="color:#e6db74">&#39;Linux version&#39;</span> <span style="color:#f92672">||</span> echo<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> -z <span style="color:#e6db74">&#34;</span>$output<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># compressed kernel, needs more work, see https://raspberrypi.stackexchange.com/a/108107</span>
</span></span><span style="display:flex;"><span>        pos<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>LC_ALL<span style="color:#f92672">=</span>C grep -P -a -b -m <span style="color:#ae81ff">1</span> --only-matching <span style="color:#e6db74">&#39;\x1f\x8b\x08&#39;</span> $kernelimg | cut -f <span style="color:#ae81ff">1</span> -d :<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>        dd <span style="color:#66d9ef">if</span><span style="color:#f92672">=</span>$kernelimg of<span style="color:#f92672">=</span>kernel.gz skip<span style="color:#f92672">=</span>$pos iflag<span style="color:#f92672">=</span>skip_bytes
</span></span><span style="display:flex;"><span>        output<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>gzip --decompress --stdout kernel.gz | strings | grep <span style="color:#e6db74">&#39;Linux version&#39;</span> <span style="color:#f92672">||</span> echo<span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    version<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>echo $output | awk <span style="color:#e6db74">&#39;{print $3}&#39;</span> | tr -d <span style="color:#e6db74">&#39;+&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>    build<span style="color:#f92672">=</span><span style="color:#66d9ef">$(</span>echo $output | awk -F<span style="color:#e6db74">&#34;#&#34;</span> <span style="color:#e6db74">&#39;{print $NF}&#39;</span> | awk <span style="color:#e6db74">&#39;{print $1}&#39;</span><span style="color:#66d9ef">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#f92672">[[</span> -n <span style="color:#e6db74">&#34;</span>$version<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&amp;&amp;</span> -n <span style="color:#e6db74">&#34;</span>$build<span style="color:#e6db74">&#34;</span> <span style="color:#f92672">]]</span>; <span style="color:#66d9ef">then</span>
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#34;Version: </span>$kernel<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#34;Build: </span>$build<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">else</span>
</span></span><span style="display:flex;"><span>        echo
</span></span><span style="display:flex;"><span>        echo <span style="color:#e6db74">&#34;Cannot determine kernel version and build number for </span>$kernelimg<span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p>Note that this has only been tested with kernels on RaspberryPi OS images, YMMV.</p>
]]></content:encoded></item></channel></rss>