<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kengah Ireneaus</title>
    <description>The latest articles on DEV Community by Kengah Ireneaus (@4kit1-glitch).</description>
    <link>https://dev.to/4kit1-glitch</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3775003%2Fc3d04ce3-2b37-41a3-840a-1c448d9c8313.jpg</url>
      <title>DEV Community: Kengah Ireneaus</title>
      <link>https://dev.to/4kit1-glitch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/4kit1-glitch"/>
    <language>en</language>
    <item>
      <title>sysk a CLI health monitor in bash and python, how i built it</title>
      <dc:creator>Kengah Ireneaus</dc:creator>
      <pubDate>Tue, 01 Sep 2026 17:55:18 +0000</pubDate>
      <link>https://dev.to/4kit1-glitch/sysk-a-cli-health-monitor-in-bash-and-python-how-i-built-it-361h</link>
      <guid>https://dev.to/4kit1-glitch/sysk-a-cli-health-monitor-in-bash-and-python-how-i-built-it-361h</guid>
      <description>&lt;h2&gt;
  
  
  Sysk architecture
&lt;/h2&gt;

&lt;p&gt;Design notes on Sysk, a small system health monitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Sysk is a command-line system health monitor built in Bash and Python.&lt;/p&gt;

&lt;p&gt;Bash collects machine data, handles flags, and drives the user interface. Python runs the inference and decision engines.&lt;/p&gt;

&lt;p&gt;This article records the design of v1: what the system does, which decisions shaped it, and where those decisions cost something.&lt;/p&gt;

&lt;h2&gt;
  
  
  System design
&lt;/h2&gt;

&lt;p&gt;The pipeline is four stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collect --&amp;gt; infer --&amp;gt; decide --&amp;gt; print
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;v1 collects data across five modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cpu&lt;/li&gt;
&lt;li&gt;disk&lt;/li&gt;
&lt;li&gt;memory&lt;/li&gt;
&lt;li&gt;thermal&lt;/li&gt;
&lt;li&gt;sound&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each module presented a different reading problem. The rest of the article follows the pipeline above.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Data collection (Bash)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Decision 1: read kernel files instead of running helper programs
&lt;/h3&gt;

&lt;p&gt;The main collection decision was to read kernel-exported files rather than treat programs such as &lt;code&gt;top&lt;/code&gt; or &lt;code&gt;free&lt;/code&gt; as the source of truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fewer extra binaries, so fewer runtime dependencies&lt;/li&gt;
&lt;li&gt;one read model per module&lt;/li&gt;
&lt;li&gt;data taken from the same place the kernel already exposes&lt;/li&gt;
&lt;li&gt;no wait for a helper tool to start and format output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you parse kernel-formatted text yourself&lt;/li&gt;
&lt;li&gt;each file has its own layout, so the learning curve is steep&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Files and directories used&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/proc/meminfo          -&amp;gt; memory
/proc/cpuinfo          -&amp;gt; cpu identity
/proc/loadavg          -&amp;gt; load average
/proc/stat             -&amp;gt; cpu accounting
/proc/uptime           -&amp;gt; uptime

/sys/block             -&amp;gt; disks
/sys/class/thermal     -&amp;gt; thermal zones and cooling
/sys/class/hwmon       -&amp;gt; hardware monitors (fans, sensors)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This rule is not absolute. Hardware identity and sound did not fit it cleanly. &lt;code&gt;dmidecode&lt;/code&gt;, &lt;code&gt;smartctl&lt;/code&gt;, and &lt;code&gt;pactl&lt;/code&gt; are still dependencies. Sound, in particular, is read through &lt;code&gt;pactl&lt;/code&gt; rather than a kernel file.&lt;/p&gt;

&lt;p&gt;Finding the right file for each module was messy. Once the path was known, the output looked cryptic at first glance, but it was stable enough to parse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 2: many scripts, one entry point
&lt;/h3&gt;

&lt;p&gt;Reading files raised the next question: how should the project be structured?&lt;/p&gt;

&lt;p&gt;Each major function became its own script, sourced from a single entry point. That was easy to say and immediately produced four portability questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How does the program find itself, no matter where it is launched from?&lt;/li&gt;
&lt;li&gt;How do scripts share variables without hard-coding absolute paths?&lt;/li&gt;
&lt;li&gt;In what order should scripts be sourced so that dependencies exist before they are used?&lt;/li&gt;
&lt;li&gt;What does “runs on another machine” actually require?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those questions produced the portable layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Resolve the application path&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the usual “where am I?” idiom. A pure string-manipulation version exists, but the &lt;code&gt;cd&lt;/code&gt; / &lt;code&gt;pwd -P&lt;/code&gt; form follows symlinks and is the one Sysk uses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Export directory names from that root&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;XDG_CONFIG_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;XDG_CONFIG_HOME&lt;/span&gt;&lt;span class="k"&gt;:-${&lt;/span&gt;&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/.config&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DUMPS_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMPS_PATH&lt;/span&gt;&lt;span class="k"&gt;:-${&lt;/span&gt;&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/logs&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CACHE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CACHE_PATH&lt;/span&gt;&lt;span class="k"&gt;:-${&lt;/span&gt;&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/.cache&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CACHE_PATH&lt;/span&gt;&lt;span class="s2"&gt;/sysk"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ENGINE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENGINE_DIR&lt;/span&gt;&lt;span class="k"&gt;:-${&lt;/span&gt;&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/engine&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RULE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENGINE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/.rules"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RESULT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENGINE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/results"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y_%m_%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Defaults can be overridden by the environment. Paths are derived from &lt;code&gt;SCRIPT_DIR&lt;/code&gt; instead of being pasted in as &lt;code&gt;/home/kit/...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Source scripts in a fixed order&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;core -&amp;gt; device scripts -&amp;gt; hardware scripts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;script &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CORE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.sh&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done

for &lt;/span&gt;&lt;span class="nb"&gt;dir &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEVICE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    for &lt;/span&gt;script &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.sh&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;done
done

for &lt;/span&gt;script &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HW_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.sh&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Core files are numbered so the source order is obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00_error.sh
01_privileged.sh
02_check_deps.sh
03_install_deps.sh
04_cleanup.sh
05_parse_flags.sh
tui.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;00_error.sh&lt;/code&gt; — named error codes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;01_privileged.sh&lt;/code&gt; — root / sudo handling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;02_check_deps.sh&lt;/code&gt; — missing binaries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;03_install_deps.sh&lt;/code&gt; — install those binaries&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;04_cleanup.sh&lt;/code&gt; — cleanup and traps&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;05_parse_flags.sh&lt;/code&gt; — &lt;code&gt;getopts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tui.sh&lt;/code&gt; — text interface&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Privileged work
&lt;/h3&gt;

&lt;p&gt;Some reads (&lt;code&gt;dmidecode&lt;/code&gt;, parts of disk data) need elevated permission. A one-line &lt;code&gt;sudo "$@"&lt;/code&gt; was not enough. The questions were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what if the user is already root?&lt;/li&gt;
&lt;li&gt;what if &lt;code&gt;sudo&lt;/code&gt; is missing?&lt;/li&gt;
&lt;li&gt;what if the user refuses permission?&lt;/li&gt;
&lt;li&gt;how do you drop sudo again after the privileged step?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;01_privileged.sh&lt;/code&gt; answers those with small predicates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;is_root&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$EUID&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_SUCCESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

is_sudo_available&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_SUCCESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

is_sudo_active&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_SUCCESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

kill_sudo&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    is_sudo_active &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_SUCCESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'sudo not active, aborting...\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_BAD_USAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking and installing dependencies
&lt;/h3&gt;

&lt;p&gt;Even with a file-first design, Sysk still depends on a few programs: &lt;code&gt;dmidecode&lt;/code&gt;, &lt;code&gt;smartctl&lt;/code&gt;, &lt;code&gt;pactl&lt;/code&gt;, and &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;check dependencies -&amp;gt; get permission -&amp;gt; install missing ones
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Store the required names in an array.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;command -v&lt;/code&gt; on each name; keep the missing ones.&lt;/li&gt;
&lt;li&gt;Ask for permission.&lt;/li&gt;
&lt;li&gt;Detect the package manager and install the missing set.&lt;/li&gt;
&lt;li&gt;Confirm the set is present.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;PACKAGE_MANAGERS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    apt dnf pacman zypper emerge
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;os_pkg_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

get_pkg_manager&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;pkg &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PACKAGE_MANAGERS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pkg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nv"&gt;os_pkg_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$pkg&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;0
        &lt;span class="k"&gt;fi
    done
    &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'unknown package manager, aborting...\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Collection per module
&lt;/h3&gt;

&lt;p&gt;Each module binds the files it cares about and hides the parsing behind a writer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CPU&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;CPU_INFO_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/proc/cpuinfo"&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;LOAD_INFO_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/proc/loadavg"&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;CPU_USAGE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/proc/stat"&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;UPTIME_INFO_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/proc/uptime"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Memory&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;MEM_INFO_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/proc/meminfo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Disks&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;DRIVE_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/sys/block"&lt;/span&gt;

&lt;span class="nb"&gt;mapfile&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; drives &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;
    find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DRIVE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="nt"&gt;-printf&lt;/span&gt; &lt;span class="s1"&gt;'%f\n'&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Ev&lt;/span&gt; &lt;span class="s1"&gt;'(^loop|ram|zram)'&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Thermal&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;SYSTEM_THERMAL_ZONE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/sys/class/thermal/thermal_zone"&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;SYSTEM_FAN_INFO_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/sys/class/hwmon"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sound&lt;/strong&gt; — collected through &lt;code&gt;pactl&lt;/code&gt;, not &lt;code&gt;/proc&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing collected data
&lt;/h3&gt;

&lt;p&gt;Snapshots are JSON, written with &lt;code&gt;jq&lt;/code&gt; and a per-module filter file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;write_memory_json&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;MEM_CONFIG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/memory_&lt;/span&gt;&lt;span class="nv"&gt;$DATE&lt;/span&gt;&lt;span class="s2"&gt;.json"&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CONFIG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    jq &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--arg&lt;/span&gt; total_mem &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_total_mem&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--arg&lt;/span&gt; available_mem &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;get_available_mem&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FEATURE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/build_memory.jq"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MEM_CONFIG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example thermal snapshot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"average_temp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;31.87&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"thermal_zones"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"zone_temparatures"&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="nl"&gt;"acpitz"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"29.80"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"x86_pkg_temp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"38.00"&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="nl"&gt;"cooling"&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="nl"&gt;"fan_zones"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fan_speed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fan1_input=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s2"&gt;fan2_input=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Problem.&lt;/strong&gt; The JSON shape is not the same across modules. Some values are scalars, some are objects, some are nested. That leaked into inference and into the rule files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix for v1.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each module has its own rule file&lt;/li&gt;
&lt;li&gt;each module has its own &lt;code&gt;build_*.jq&lt;/code&gt; filter&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;source&lt;/code&gt; field in the YAML tells the engine where to read&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;resolve&lt;/code&gt; function walks that path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A uniform snapshot schema is still future work.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Inference and decision (Python)
&lt;/h2&gt;

&lt;p&gt;Inference pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;load data -&amp;gt; load rules -&amp;gt; set status per field -&amp;gt; write result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine is a Python script. It uses the standard library plus PyYAML, and it reads the same environment variables Bash exported (&lt;code&gt;CACHE_PATH&lt;/code&gt;, &lt;code&gt;RULE_DIR&lt;/code&gt;, &lt;code&gt;RESULT_DIR&lt;/code&gt;, &lt;code&gt;DATE&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_rules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rule_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;rule_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;data_path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;OSError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[ERROR] failed to load &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules live in &lt;code&gt;engine/.rules&lt;/code&gt; as YAML. Each field names the value it wants through &lt;code&gt;source&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu&lt;/span&gt;
&lt;span class="na"&gt;check_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="na"&gt;system_vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cpu_cores&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto"&lt;/span&gt;

&lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu_usage_percent&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage"&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;percentage"&lt;/span&gt;
    &lt;span class="na"&gt;base_value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
    &lt;span class="na"&gt;warning_multiplier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.83&lt;/span&gt;
    &lt;span class="na"&gt;critical_multiplier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.90&lt;/span&gt;
    &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%"&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;load_avg5&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;load_avg.1"&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;load"&lt;/span&gt;
    &lt;span class="na"&gt;base_value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cores"&lt;/span&gt;
    &lt;span class="na"&gt;warning_multiplier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;critical_multiplier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.5&lt;/span&gt;
    &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The collector JSON could not be renamed. The mapping therefore lives in the rules, not in a hard-coded Python dictionary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 3: a path language instead of one parser per module
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;source&lt;/code&gt; is a small path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a bare key for a top-level field — &lt;code&gt;usage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;dots for nesting — &lt;code&gt;core_info.cores_usage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;an integer segment for a list index — &lt;code&gt;load_avg.1&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;resolve&lt;/code&gt; walks that path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_list_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_dict_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The helpers catch &lt;code&gt;KeyError&lt;/code&gt;, &lt;code&gt;IndexError&lt;/code&gt;, and &lt;code&gt;TypeError&lt;/code&gt;, print on stderr, and exit. &lt;code&gt;resolve&lt;/code&gt; itself stays a straight loop.&lt;/p&gt;

&lt;p&gt;Because cpu, memory, and thermal still do not share one value shape, &lt;code&gt;evaluate&lt;/code&gt; dispatches to a small private helper per module (&lt;code&gt;_evaluate_cpu&lt;/code&gt;, and so on) and merges the results into one dictionary. v1 loads the three modules it already has rules for. Disk and sound collection exist; their inference rules are not finished in the same way.&lt;/p&gt;

&lt;p&gt;The public &lt;code&gt;main&lt;/code&gt; in &lt;code&gt;inference.py&lt;/code&gt; is only orchestration: check environment variables, check input paths, create &lt;code&gt;RESULT_DIR&lt;/code&gt; if needed, evaluate, write &lt;code&gt;result_{DATE}.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision engine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;load result -&amp;gt; find warning and critical fields -&amp;gt; write a cause log if needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The decision script does not re-parse &lt;code&gt;/proc&lt;/code&gt;. It reads today’s result file.&lt;/p&gt;

&lt;p&gt;The decision I made for v1: if every field is &lt;code&gt;OK&lt;/code&gt;, exit 0 and write nothing extra. If any field is not &lt;code&gt;OK&lt;/code&gt;, write &lt;code&gt;cause_{DATE}.log&lt;/code&gt; and return a non-zero status (90) so Bash can tell an alert from a clean run.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Flags, display, and the entry point
&lt;/h2&gt;

&lt;p&gt;v1 uses short flags only, parsed with &lt;code&gt;getopts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;parse_args&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s1"&gt;':hvrm:'&lt;/span&gt; opt&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
        case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$opt&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
            &lt;/span&gt;h&lt;span class="p"&gt;)&lt;/span&gt; usage&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt;
            v&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s version %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROGNAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt;
            &lt;span class="c"&gt;# ...&lt;/span&gt;
        &lt;span class="k"&gt;esac&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Action flags such as &lt;code&gt;-h&lt;/code&gt; and &lt;code&gt;-v&lt;/code&gt; print and exit inside the parser. Setting flags only store values. The TUI runs only when the session is a terminal and quiet mode is off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main procedure
&lt;/h3&gt;

&lt;p&gt;All of the pieces meet in one function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;check files and directories
parse flags
install missing dependencies
collect snapshots
run inference
run decision
show the TUI unless -q was passed
exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    check_req_files &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    check_req_dirs  &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    parse_args &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    install_missing_deps

    write_cpu_json &amp;amp;
    write_memory_json
    write_thermal_json
    write_disk_json
    write_sound_json

    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DUMPS_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENGINE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/inference.py"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENGINE_DIR&lt;/span&gt;&lt;span class="s2"&gt;/decision.py"&lt;/span&gt;

    display_tui &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_SUCCESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

main &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Decision 4: collect in the background where it hurts
&lt;/h3&gt;

&lt;p&gt;The first runs collected modules one after another. CPU sampling waits about one second to compute usage, so the whole collect stage felt slow.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;write_cpu_json &amp;amp;&lt;/code&gt; starts that slow job in the background so the other writers can proceed. That made the collect stage feel almost instant.&lt;/p&gt;

&lt;p&gt;This only stays correct if the entry point waits for the background job before inference starts. Otherwise Python can open a CPU file that is not finished yet. A &lt;code&gt;wait&lt;/code&gt; after the writers is part of this design, not an optional extra.&lt;/p&gt;




&lt;h2&gt;
  
  
  What v1 is not
&lt;/h2&gt;

&lt;p&gt;The project is small on purpose.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no long options&lt;/li&gt;
&lt;li&gt;no previous-day comparison of result files&lt;/li&gt;
&lt;li&gt;no uniform JSON schema across modules&lt;/li&gt;
&lt;li&gt;disk and sound are collected; they are not fully inferred like cpu, memory, and thermal&lt;/li&gt;
&lt;li&gt;the TUI is a banner and a status list, not a full-screen toolkit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dated result files are already named &lt;code&gt;result_{DATE}.json&lt;/code&gt; so a later version can compare runs without changing the collector.&lt;/p&gt;




&lt;h2&gt;
  
  
  Layout
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sysk/
├── lib/
│   ├── core/          # 00_error.sh ... 05_parse_flags.sh, tui.sh
│   ├── devices/
│   └── hw/
├── engine/
│   ├── .rules/        # cpu.yml, memory.yml, thermal.yml
│   ├── inference.py
│   ├── decision.py
│   └── results/
└── sysk               # entry point
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the architecture of v1: files in, JSON snapshots, YAML rules, a small path walker, a verdict, and a Bash wrapper that the user actually types. Many features could still be added. The shape above is what is running now.&lt;/p&gt;

&lt;p&gt;if you are interested in the project you can check it out at &lt;a href="https://github.com/4kit1-glitch/sysk" rel="noopener noreferrer"&gt;https://github.com/4kit1-glitch/sysk&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;thanks for being along with me in my journey&lt;/p&gt;

</description>
      <category>bash</category>
      <category>automation</category>
      <category>python</category>
      <category>linux</category>
    </item>
    <item>
      <title>Defensive Scripting with bash</title>
      <dc:creator>Kengah Ireneaus</dc:creator>
      <pubDate>Mon, 31 Aug 2026 15:08:00 +0000</pubDate>
      <link>https://dev.to/4kit1-glitch/defensive-scripting-with-bash-2o3a</link>
      <guid>https://dev.to/4kit1-glitch/defensive-scripting-with-bash-2o3a</guid>
      <description>&lt;h2&gt;
  
  
  DEFENSIVE PROGRAMMING IN BASH
&lt;/h2&gt;

&lt;p&gt;Bash is a very powerful tool, but it is important to employ strategies that catch errors early, provide clear logging, and handle unexpected situations cleanly.&lt;/p&gt;

&lt;p&gt;The scripts you write may be subjected to unexpected behaviours and events such as a required directory not present, a required shell command not present, or, commonly, permission errors.&lt;/p&gt;

&lt;p&gt;I have been writing Bash scripts for a while, and I have gathered the best techniques from structuring scripts to seamless error handling, which we will explore: some key practices to enhance reliability of your scripts.&lt;/p&gt;

&lt;p&gt;This guide assumes prior exposure to Bash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structuring a Bash script
&lt;/h2&gt;

&lt;p&gt;Bash being a flexible language as it is follows no strict structure, but here is the one I recommend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# global variables immutable and referenced by whole script&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;PROGNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# sourced script&lt;/span&gt;
&lt;span class="nb"&gt;source &lt;/span&gt;my_script.sh

&lt;span class="c"&gt;# program logic grouped into functions&lt;/span&gt;
do_something&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# logic&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# main procedure&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# calling main &lt;/span&gt;
main &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"program failed"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  export vs local vs global
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Try to keep global variables to a minimum&lt;/li&gt;
&lt;li&gt;SHOUTING_SNAKE_CASE VAR NAMING&lt;/li&gt;
&lt;li&gt;readonly declaration&lt;/li&gt;
&lt;li&gt;use variable names to replace cryptic &lt;code&gt;$1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Globals I recommend you always use in your programs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;PROG_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;PROG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;readlink&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
&lt;span class="c"&gt;# or even better &lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sourcing and splitting a script
&lt;/h2&gt;

&lt;p&gt;Splitting the logic of your script into multiple scripts will show easier to manage as the script gets larger.&lt;/p&gt;

&lt;p&gt;All other variables should be local.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
change_owner_of_file&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

    &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;:&lt;span class="nv"&gt;$group&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modular coding, functions and arguments
&lt;/h2&gt;

&lt;p&gt;Grouping procedures into functions and specifying its return code in case of failure or success is the way to go.&lt;br&gt;
Some distinctive functions I use are:&lt;/p&gt;
&lt;h3&gt;
  
  
  info() -- for logging successful events
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;track all changes made by script, especially cron jobs
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

info&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[INFO] [&lt;/span&gt;&lt;span class="nv"&gt;$TIME&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$info_logs&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# or you can use separate info files for each event, but that might take up space when doing scripting on small-space devices like Raspberry Pi boards &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  error() -- for logging failed events and warnings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;error logging very important for building daemons&lt;/li&gt;
&lt;li&gt;easily get reason why script may have failed mid-run
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

error&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ERROR] [&lt;/span&gt;&lt;span class="nv"&gt;$TIME&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$err_logs&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  main() -- single point of entry for your program
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;keeps code clean&lt;/li&gt;
&lt;li&gt;procedures become descriptive
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# check req files&lt;/span&gt;
    &lt;span class="c"&gt;# check req dirs&lt;/span&gt;
    &lt;span class="c"&gt;# install missing deps&lt;/span&gt;
    &lt;span class="c"&gt;# confirm installation&lt;/span&gt;
    &lt;span class="c"&gt;# run data collections&lt;/span&gt;
    &lt;span class="c"&gt;# run inference engine and generate results&lt;/span&gt;
    &lt;span class="c"&gt;# run decision engine and generate results&lt;/span&gt;
    &lt;span class="c"&gt;# run tui if -q flag isn't given, which just pretty-print results and decision&lt;/span&gt;
    &lt;span class="c"&gt;# exit &lt;/span&gt;
    check_req_files &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Required files missing"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    check_req_dirs &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"Required directories missing"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    parse_args &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="c"&gt;# installing missing dependencies&lt;/span&gt;
    install_missing_deps

    &lt;span class="c"&gt;# running data collections&lt;/span&gt;
    write_cpu_json &amp;amp;
    write_memory_json
    write_thermal_json
    write_disk_json 
    write_sound_json

    &lt;span class="c"&gt;# run engine&lt;/span&gt;
    python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENGINE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/inference.py &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENGINE_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/decision.py

    display_tui &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_FAILURE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ERR_SUCCESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

main &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# code taken from sysk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Commenting
&lt;/h2&gt;

&lt;p&gt;From my experience, Bash syntax isn't really presentable. It gets messy fast as the program expands. All the cryptic &lt;code&gt;?*&amp;amp;&amp;amp;^^&lt;/code&gt; symbols become confusing, because a single symbol can be used in 2 or more cases representing different things.&lt;/p&gt;

&lt;p&gt;A simple comment &lt;code&gt;# this does this&lt;/code&gt; will go a long way.&lt;/p&gt;

&lt;p&gt;giving strict instructions:&lt;br&gt;
&lt;code&gt;# DONT TOUCH THIS&lt;/code&gt; &lt;code&gt;# USE PRINTF NOT ECHO&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrapping lines
&lt;/h2&gt;

&lt;p&gt;When using a code editor like VS Code, they give you automatic wrap-line options.&lt;br&gt;
Yes, it works, but when migrating the code to another IDE it breaks.&lt;br&gt;
So if code is too long, wrap with the &lt;code&gt;\&lt;/code&gt;.&lt;br&gt;
Also makes script look clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--arg&lt;/span&gt; name &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$names&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  /dev/null and /dev/zero
&lt;/h2&gt;

&lt;p&gt;Think of &lt;code&gt;/dev/null&lt;/code&gt; as a black hole. It is essentially the equivalent of a write-only file.&lt;br&gt;
Everything written to it disappears. Attempts to read or output from it result in nothing.&lt;br&gt;
Suppressing stdout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;span class="c"&gt;# Contents of the file will not list to stdout.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can be very useful when you need a command to run without any output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging
&lt;/h2&gt;

&lt;p&gt;Bash has no built-in debugger, but there are many workarounds.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;using &lt;code&gt;shellcheck&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;ShellCheck is one of the best ways to write a safe script. Sometimes it might be annoying to use because of the many options, but a script which can pass through ShellCheck successfully has probably no syntax error. But it can't catch runtime errors or logic errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;using &lt;code&gt;set -x&lt;/code&gt; and &lt;code&gt;set +x&lt;/code&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;this echoes each line before producing output&lt;/li&gt;
&lt;li&gt;use to debug a particular piece of code where you feel the error might be from
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"use case"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt; +x
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;using &lt;code&gt;sh -n scriptname&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;catches syntax errors without executing script&lt;/li&gt;
&lt;li&gt;some syntax errors may escape&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Handling failures and testing using if statements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting error-handling options
&lt;/h3&gt;

&lt;p&gt;Bash is set by default to run to completion even when it encounters an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"program starts"&lt;/span&gt;
ech &lt;span class="s2"&gt;"program continues"&lt;/span&gt; &lt;span class="c"&gt;# command doesn't exist&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"program end"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this gives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;program starts
./test.sh: line 3: ech: &lt;span class="nb"&gt;command &lt;/span&gt;not found
program end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This and other common unexpected behaviours can be avoided using shell options.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;set -o errexit&lt;/code&gt; or &lt;code&gt;set -e&lt;/code&gt;: this option ensures that the script exits when any command returns a non-zero status unless handled with &lt;code&gt;||&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set -o nounset&lt;/code&gt; or &lt;code&gt;set -u&lt;/code&gt;: ensures the script treats unset variables as errors&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set -o pipefail&lt;/code&gt;: this option causes the pipeline to fail if any command within it fails&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These commands can be combined as &lt;code&gt;set -euo pipefail&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"program starts"&lt;/span&gt;
ech &lt;span class="s2"&gt;"program continues"&lt;/span&gt; &lt;span class="c"&gt;# command doesn't exist&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"program end"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;program starts
./test.sh: line 4: ech: &lt;span class="nb"&gt;command &lt;/span&gt;not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Some problems with the set -euo pipefail
&lt;/h2&gt;

&lt;p&gt;While it might seem very convenient to use the guard line, there are some unexpected behaviours which might be unwanted, especially with the &lt;code&gt;set -e&lt;/code&gt; flag, which is very problematic.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Conditions disable set -e 

&lt;ul&gt;
&lt;li&gt;the test of if, while and until &lt;/li&gt;
&lt;li&gt;on the left of &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;inverted with the ! flag
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;never &lt;span class="c"&gt;# false fails but script continues&lt;/span&gt;
&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;this runs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Command substitution and subshells don't automatically inherit the set -e flag:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; 
&lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"gets here"&lt;/span&gt; &lt;span class="c"&gt;# still successfully runs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be fixed with &lt;code&gt;shopt -s inherit_errexit&lt;/code&gt; shell option (Bash 4.4+).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;local&lt;/code&gt;, &lt;code&gt;export&lt;/code&gt; and &lt;code&gt;readonly&lt;/code&gt;: they mask return values so they always succeed even if substituted command fails
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;answer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find /bin/bash&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="c"&gt;# bad&lt;/span&gt;
&lt;span class="c"&gt;# better&lt;/span&gt;
&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$()&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;word
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be fixed by declaring and assigning on separate lines.&lt;/p&gt;

&lt;p&gt;This demonstrates set -e can give a fake sense of safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explicitly catching and handling errors
&lt;/h2&gt;

&lt;p&gt;As shown above, the shell options are a good way to perform error handling in a script, but they are not perfect. That said, the best way to handle errors is to use conditionals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In Bash, an exit status of 0 represents success; any other represents a failure, but there are special exit codes that specify a particular event.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The exit code of the last command to run is stored in the &lt;code&gt;$?&lt;/code&gt; special variable.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello world"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# outputs 0 &lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# some special return codes&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;ERR_SUCCESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0  &lt;span class="c"&gt;# operation successful&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;ERR_FAILURE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1  &lt;span class="c"&gt;# operation failed general &lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;ERR_PERMISSION_DENIED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;126 &lt;span class="c"&gt;# permission denied&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;ERR_COMMAND_FAILED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;126 &lt;span class="c"&gt;# command runs but fails&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;ERR_NOT_FOUND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;127  &lt;span class="c"&gt;# command or file not found&lt;/span&gt;
&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;ERR_BAD_USAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2    &lt;span class="c"&gt;# bad use of script or function&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Catching these codes with &lt;code&gt;||&lt;/code&gt; and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; is the way to go.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"no file with name &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;127
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling unexpected exit events using traps
&lt;/h2&gt;

&lt;p&gt;Trap exit events to perform cleanup tasks or handle unexpected&lt;br&gt;
&lt;code&gt;trap [command] [signals]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's take a script designed to run non-stop for 60 seconds, maybe performing data movement or log parsing, and we don't want any interruption, maybe from &lt;code&gt;Ctrl + C&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s2"&gt;"echo Booh!"&lt;/span&gt; SIGINT SIGTERM
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"pid is &lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;sleep &lt;/span&gt;60    &lt;span class="c"&gt;# script last 60 seconds&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You can do more research on traps.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Following these practices will ensure to a certain level that your Bash script will be robust.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/better-programming/defencsive-programming-to-create-a-robust-ish-shell-script-d9f21292d08a" rel="noopener noreferrer"&gt;Defensive Programming to create a robust shell script by prabesh on medium&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/banks/stop-ignoring-errors-in-bash-3co5"&gt;stop ignoring errors in bash&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Note: this article summarises my discoveries with Bash. There are many pitfalls and gotchas which have not been mentioned here.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remember there is no perfect guide to Bash. The best way to learn is to practice and encounter the errors yourself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Thanks for reading to this level. If you have any doubts, please don't hesitate to share in the comments. Happy scripting.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>bash</category>
      <category>beginners</category>
      <category>automation</category>
      <category>linux</category>
    </item>
    <item>
      <title>A Practical Guide to jq in Shell Scripts</title>
      <dc:creator>Kengah Ireneaus</dc:creator>
      <pubDate>Sun, 16 Aug 2026 18:01:26 +0000</pubDate>
      <link>https://dev.to/4kit1-glitch/a-practical-guide-to-jq-in-shell-scripts-dp6</link>
      <guid>https://dev.to/4kit1-glitch/a-practical-guide-to-jq-in-shell-scripts-dp6</guid>
      <description>&lt;h2&gt;
  
  
  A Practical Guide to jq in Shell Scripts
&lt;/h2&gt;

&lt;p&gt;While building &lt;code&gt;sykd&lt;/code&gt; (a system health monitor daemon for linux servers) i needed to collect system data as json so i could process easily in python on doing research if found out about the &lt;code&gt;jq shell program&lt;/code&gt; for creating and parsing json in bash, but learning the commands was quite tedious and the man page is large and very difficult to read so i came out with this guide to ease and summarize the major commands you might need.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jq&lt;/code&gt; is a lightweight and flexible command-line JSON processor. It is one of the most useful tools for working with JSON data directly inside shell scripts and pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debian / Ubuntu&lt;/strong&gt;: &lt;code&gt;sudo apt install jq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: &lt;code&gt;brew install jq&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other systems&lt;/strong&gt;: Use your package manager or download binaries from the official site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Basic Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="o"&gt;[&lt;/span&gt;options] &lt;span class="s1"&gt;'&amp;lt;filter&amp;gt;'&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;file...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Input can come from a file, a pipe, or standard input. The filter expression selects or transforms the data.&lt;/p&gt;

&lt;p&gt;Quick note: &lt;code&gt;jq&lt;/code&gt; reads JSON from stdin by default. If you want to generate JSON inside &lt;code&gt;jq&lt;/code&gt; without reading input, use &lt;code&gt;-n&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful Options
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raw output (no JSON quotes around strings)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compact (single-line) output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start with &lt;code&gt;null&lt;/code&gt; input (useful for generating JSON)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Slurp entire input into an array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--arg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass a string variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--argjson&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass a JSON value (preferred for arrays/objects)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Other helpful runtime options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--rawfile name filename&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read an entire file as a raw string into variable &lt;code&gt;$name&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--slurpfile name filename&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read file and slurp into array &lt;code&gt;$name&lt;/code&gt; (like &lt;code&gt;-s&lt;/code&gt; but into variable)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example: &lt;code&gt;jq --slurpfile all files.json '.[0] + $all[0]'&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Essential Filters
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Filter Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pretty-print&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract a field&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.name&lt;/code&gt; or &lt;code&gt;.["name"]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nested field&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.user.address.city&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Array element&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.[0]&lt;/code&gt; or &lt;code&gt;.[-1]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iterate array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.[]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Select matching objects&lt;/td&gt;
&lt;td&gt;`.[] \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build a new object&lt;/td&gt;
&lt;td&gt;{% raw %}&lt;code&gt;{name, age}&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transform every element&lt;/td&gt;
&lt;td&gt;&lt;code&gt;map(.name)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Length&lt;/td&gt;
&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List keys&lt;/td&gt;
&lt;td&gt;&lt;code&gt;keys&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional&lt;/td&gt;
&lt;td&gt;&lt;code&gt;if .active then "yes" else "no" end&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can define reusable functions inside &lt;code&gt;jq&lt;/code&gt; using &lt;code&gt;def&lt;/code&gt;. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def greet: "Hello, " + .name + "!";
. | greet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output &lt;code&gt;"Hello, &amp;lt;name&amp;gt;!"&lt;/code&gt; for an object with a &lt;code&gt;name&lt;/code&gt; key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Examples
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pretty-print a file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nb"&gt;.&lt;/span&gt; data.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Extract a single value&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.version'&lt;/span&gt; package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Filter an array of objects&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.[] | select(.status == "active") | .name'&lt;/span&gt; users.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a new structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'{names: map(.name), total: length}'&lt;/span&gt; people.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use in a pipeline&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.example.com/data | jq &lt;span class="s1"&gt;'.results[] | .id'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Generate JSON from scratch&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'{name: "example", version: 1}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create multiple objects programmatically&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'[range(3) | {index: ., squared: (. * .)}]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces:&lt;/p&gt;

&lt;p&gt;[&lt;br&gt;
   {"index":0,"squared":0},&lt;br&gt;
   {"index":1,"squared":1},&lt;br&gt;
   {"index":2,"squared":4}&lt;br&gt;
]&lt;/p&gt;
&lt;h2&gt;
  
  
  Passing a Bash Array to jq
&lt;/h2&gt;

&lt;p&gt;Bash arrays cannot be passed directly. Convert the array to a JSON array first, then supply it with &lt;code&gt;--argjson&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Recommended Method
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;my_array&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"apple"&lt;/span&gt; &lt;span class="s2"&gt;"banana"&lt;/span&gt; &lt;span class="s2"&gt;"cherry"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Convert bash array → JSON array&lt;/span&gt;
&lt;span class="nv"&gt;json_array&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;my_array&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; | jq &lt;span class="nt"&gt;-s&lt;/span&gt; .&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Use it inside jq&lt;/span&gt;
jq &lt;span class="nt"&gt;--argjson&lt;/span&gt; arr &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_array&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'$arr'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;One-liner version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;--argjson&lt;/span&gt; arr &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;my_array&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; | jq &lt;span class="nt"&gt;-s&lt;/span&gt; .&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="s1"&gt;'$arr[]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Numeric Arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;10 20 30&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;json_numbers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-s&lt;/span&gt; .&lt;span class="si"&gt;)&lt;/span&gt;

jq &lt;span class="nt"&gt;--argjson&lt;/span&gt; nums &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_numbers&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'$nums | map(. * 2)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feed as Input Instead
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;my_array&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; | jq &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tips for Reliable Shell Scripts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always quote the filter when it contains spaces or special characters.&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;--argjson&lt;/code&gt; for arrays and objects; use &lt;code&gt;--arg&lt;/code&gt; only for plain strings.&lt;/li&gt;
&lt;li&gt;Test filters interactively before embedding them in scripts.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For complex logic, store the filter in a separate &lt;code&gt;.jq&lt;/code&gt; file and call it with &lt;code&gt;-f&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When embedding multi-line filters in shell variables, use a heredoc to keep quoting simple:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
.[] | select(.status=="active") | {id, name}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

curl &lt;span class="nt"&gt;-s&lt;/span&gt; api | jq &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prefer &lt;code&gt;--argjson&lt;/code&gt; when you need to pass non-string structured data; &lt;code&gt;--arg&lt;/code&gt; always passes a string.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When values may contain newlines or special characters, convert them to JSON with &lt;code&gt;jq -R .&lt;/code&gt; before passing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For newline-delimited JSON (NDJSON/JSONL), use &lt;code&gt;jq -c .&lt;/code&gt; to ensure compact output and &lt;code&gt;--slurp&lt;/code&gt; if you need to combine records into an array.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;try ... catch&lt;/code&gt; for safe extraction and defaults:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try .foo.bar catch "default"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;inputs&lt;/code&gt; to read multiple JSON documents from stdin (useful with &lt;code&gt;-n&lt;/code&gt;):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# echo multiple JSON objects and process them with inputs&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s
'&lt;/span&gt; &lt;span class="s1"&gt;'{"x":1}'&lt;/span&gt; &lt;span class="s1"&gt;'{"x":2}'&lt;/span&gt; | jq &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'[inputs | .x]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Additional Examples and Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Selecting and Transforming
&lt;/h3&gt;

&lt;p&gt;Extract and rename fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.[] | {identifier: .id, user: .name, active: .status == "active"}'&lt;/span&gt; users.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build an index (map id → object):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'map({ (.id|tostring): . }) | add'&lt;/span&gt; users.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Group by a key and count per group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'group_by(.category) | map({category: .[0].category, count: length})'&lt;/span&gt; items.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Working with Strings
&lt;/h3&gt;

&lt;p&gt;Trim and lowercase a field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.name | ascii_downcase | gsub("^ +| +$"; "")'&lt;/span&gt; person.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Join array of strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tags | join(",")'&lt;/span&gt; article.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Numeric operations
&lt;/h3&gt;

&lt;p&gt;Sum field values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'[.[] | .size] | add'&lt;/span&gt; files.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find maximum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'max_by(.score) | .score'&lt;/span&gt; scores.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Advanced: reduce, recurse, and more
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;reduce&lt;/code&gt; to accumulate values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'reduce .[] as $item (0; . + $item.value)'&lt;/span&gt; metrics.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recursively find all keys named &lt;code&gt;id&lt;/code&gt; in nested structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.. | objects | .id? // empty'&lt;/span&gt; nested.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Streaming large JSON (&lt;code&gt;jq --stream&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;For very large JSON documents use &lt;code&gt;--stream&lt;/code&gt; to get path/value pairs; processing is more manual but uses much less memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;--stream&lt;/span&gt; &lt;span class="s1"&gt;'select(.[0][-1]=="targetKey") | .[1]'&lt;/span&gt; large.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Working with NDJSON / JSONL
&lt;/h3&gt;

&lt;p&gt;To parse newline-delimited JSON and combine into an array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt; file.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Process each line independently (streaming):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'. | {id: .id, ok: (.value &amp;gt; 0)}'&lt;/span&gt; file.jsonl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Filters in separate &lt;code&gt;.jq&lt;/code&gt; files
&lt;/h3&gt;

&lt;p&gt;Save a complex filter to &lt;code&gt;filters.jq&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def summarize:
   {total: (map(.value) | add), count: length};

.items | map({k: .key, v: .value}) | summarize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call it from shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="nt"&gt;-f&lt;/span&gt; filters.jq data.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Passing environment variables into &lt;code&gt;jq&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Pass a shell variable as JSON (string):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"O'Reilly"&lt;/span&gt;
jq &lt;span class="nt"&gt;--arg&lt;/span&gt; name &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'. + {author: $name}'&lt;/span&gt; book.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass an environment variable that is already JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;json_obj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{"a":1}'&lt;/span&gt;
jq &lt;span class="nt"&gt;--argjson&lt;/span&gt; o &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_obj&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'. + $o'&lt;/span&gt; base.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error handling and safe accesses
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;?&lt;/code&gt; to avoid errors when a key may not exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'.maybe?.nested // "default"'&lt;/span&gt; data.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;try&lt;/code&gt; to catch runtime errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jq &lt;span class="s1"&gt;'try .items[] catch empty'&lt;/span&gt; data.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common pitfalls
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Forgetting to quote filters when they contain shell-sensitive characters — always wrap your filter in single quotes in shell.&lt;/li&gt;
&lt;li&gt;Passing arrays with &lt;code&gt;--arg&lt;/code&gt; (creates a JSON string, not an array) — use &lt;code&gt;--argjson&lt;/code&gt; or &lt;code&gt;jq -s&lt;/code&gt; conversion.&lt;/li&gt;
&lt;li&gt;Assuming &lt;code&gt;jq&lt;/code&gt; modifies files in-place — it writes to stdout; redirect output to a file to save changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;--stream&lt;/code&gt; for very large JSON that can't fit in memory.&lt;/li&gt;
&lt;li&gt;Avoid unnecessary &lt;code&gt;map&lt;/code&gt; + &lt;code&gt;add&lt;/code&gt; for simple reductions; &lt;code&gt;reduce&lt;/code&gt; can be more efficient.&lt;/li&gt;
&lt;li&gt;Use compiled filters in &lt;code&gt;-f&lt;/code&gt; files for complex repeated transformations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Examples: Real shell script snippets
&lt;/h3&gt;

&lt;p&gt;Filter API result and extract IDs into a bash array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;readarray &lt;span class="nt"&gt;-t&lt;/span&gt; ids &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://api.example.com/items"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.items[].id'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;id &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
   &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Got id: &lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass a bash array of words safely into &lt;code&gt;jq&lt;/code&gt; and filter by membership:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;words&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;alpha beta &lt;span class="s2"&gt;"complex value"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;json_words&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; | jq &lt;span class="nt"&gt;-s&lt;/span&gt; .&lt;span class="si"&gt;)&lt;/span&gt;

curl &lt;span class="nt"&gt;-s&lt;/span&gt; dataset.json | jq &lt;span class="nt"&gt;--argjson&lt;/span&gt; words &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$json_words&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'[.[] | select(.name as $n | $words | index($n))]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a small portable CLI using &lt;code&gt;jq&lt;/code&gt; and &lt;code&gt;bash&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nv"&gt;data_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.json&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="k"&gt;in
   &lt;/span&gt;list&lt;span class="p"&gt;)&lt;/span&gt;
      jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.items[] | "\(.id) - \(.name)"'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$data_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
   stats&lt;span class="p"&gt;)&lt;/span&gt;
      jq &lt;span class="s1"&gt;'{total: (.items | length), sum: (.items | map(.value) | add)}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$data_file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
   &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;file&amp;gt; {list|stats}"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;2 &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With a few core filters and the correct technique for passing arrays, &lt;code&gt;jq&lt;/code&gt; becomes a powerful and reliable tool inside any shell workflow. The patterns shown above cover the majority of day-to-day JSON processing needs.&lt;/p&gt;

&lt;p&gt;Happy scripting!&lt;/p&gt;

</description>
      <category>bash</category>
      <category>jq</category>
      <category>json</category>
      <category>devops</category>
    </item>
    <item>
      <title>A Month With Bash — Part 3: Building Projects</title>
      <dc:creator>Kengah Ireneaus</dc:creator>
      <pubDate>Tue, 04 Aug 2026 00:36:48 +0000</pubDate>
      <link>https://dev.to/4kit1-glitch/a-month-with-bash-part-3-building-projects-3od9</link>
      <guid>https://dev.to/4kit1-glitch/a-month-with-bash-part-3-building-projects-3od9</guid>
      <description>&lt;h2&gt;
  
  
  A Month With Bash — Part 3: Building Projects
&lt;/h2&gt;

&lt;p&gt;After all the expansions and syntax, I moved on to regex in bash. It wasn't too hard since I'd already worked with regex in Python, but alongside it I learned &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, and &lt;code&gt;awk&lt;/code&gt; — tools that turned out to be extremely useful for automation.&lt;/p&gt;

&lt;p&gt;I built a few mini projects and started automating some of my small day-to-day tasks. I won't go too deep into that here, but you can check out my learning-bash GitHub repo, which has all my learning scripts.&lt;/p&gt;

&lt;p&gt;From there I covered conditionals, loops, and repetitive tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally
&lt;/h2&gt;

&lt;p&gt;I learned about array variables in bash and shell options, &lt;br&gt;
went even further testing different ways of looping, and that's when I started actual project building(I am still building )&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;## looping with range functions -- somehow&lt;/span&gt;

&lt;span class="c"&gt;# python style looping {start..end}&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..10&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;    &lt;span class="c"&gt;# this uses brace expansion so using vars wont work becase of execution sequencing&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;
&lt;span class="k"&gt;done

&lt;/span&gt;clear

&lt;span class="c"&gt;## c - slyle looping &lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; i &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt; i &amp;lt; 10&lt;span class="p"&gt;;&lt;/span&gt; i++ &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="c"&gt;# variables works here well&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;## using variables to loop&lt;/span&gt;

clear
&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10
&lt;span class="nv"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2

&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq&lt;/span&gt; &lt;span class="nv"&gt;$start&lt;/span&gt; &lt;span class="nv"&gt;$stop&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="c"&gt;# this uses the seq command&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello world"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Spending so much time on bash wasn't a waste. Not only did it force me to learn a huge number of commands, it changed how I think about my own machine — most of what I used to do manually, I can now automate. That shift alone made the month worth it.&lt;br&gt;
i am still learning and trying to get the best practices and things not to do&lt;/p&gt;




&lt;p&gt;&lt;em&gt;THANK YOU FOR READING THIS FAR. That is a rough summary of me writing bash for a month there is really a lot left unsaid here but still building and learning. If you are just starting out with bash or if you haven't tried it hope this helps&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;feel free to drop questions advice and corrections&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>bash</category>
      <category>linux</category>
      <category>automation</category>
    </item>
    <item>
      <title>A Month With Bash — Part 2: Expansions</title>
      <dc:creator>Kengah Ireneaus</dc:creator>
      <pubDate>Tue, 04 Aug 2026 00:26:00 +0000</pubDate>
      <link>https://dev.to/4kit1-glitch/a-month-with-bash-part-2-expansions-3277</link>
      <guid>https://dev.to/4kit1-glitch/a-month-with-bash-part-2-expansions-3277</guid>
      <description>&lt;h2&gt;
  
  
  A Month With Bash — Part 2: Expansions
&lt;/h2&gt;

&lt;p&gt;Continuing from where I left off, the next thing I learned was special parameters in bash:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"$*"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$#&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$@&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$N&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$-&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another important concept I picked up is how bash executes shell scripts. Bash is one of those languages that interprets each line as it goes — but it doesn't stop if a line fails. It continues on unless you explicitly set &lt;code&gt;set -o pipefail&lt;/code&gt; (or &lt;code&gt;-e&lt;/code&gt;, depending on what you want it to catch).&lt;/p&gt;

&lt;p&gt;Generally, the procedure looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tokenizing&lt;/strong&gt;: splitting the line into tokens, usually split using the &lt;code&gt;IFS&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Brace expansion&lt;/strong&gt;: a mechanism by which arbitrary strings can be generated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;file&lt;span class="o"&gt;{&lt;/span&gt;1,2,3&lt;span class="o"&gt;}&lt;/span&gt;.txt
&lt;span class="c"&gt;## output: file1.txt file2.txt file3.txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bash preserves the order from left to right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tilde expansion&lt;/strong&gt;: this is where expansion of special symbols takes place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~&lt;/code&gt; represents the &lt;code&gt;HOME&lt;/code&gt; built-in variable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~+&lt;/code&gt; represents &lt;code&gt;PWD&lt;/code&gt;, the current working directory&lt;/li&gt;
&lt;li&gt;and others
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/Desktop &lt;span class="c"&gt;# this is $HOME/Desktop&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Parameter expansion&lt;/strong&gt;: introduced with the &lt;code&gt;$&lt;/code&gt; symbol.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ${} — the braces can be omitted for normal variables but not for array-type variables&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command substitution&lt;/strong&gt;: very important — it lets you assign the output of a command to a variable, and use commands inside &lt;code&gt;if&lt;/code&gt; and &lt;code&gt;for&lt;/code&gt; statements. Done with &lt;code&gt;$(command to execute)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;week_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%A&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# gets the current day of the week&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$week_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generally, &lt;code&gt;$()&lt;/code&gt; spawns a new shell instance, so it's advisable to avoid it where possible, for latency reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arithmetic expansion&lt;/strong&gt;: just from the name, this allows evaluation of arithmetic expressions and substitution of the result. It starts with &lt;code&gt;$(( expression ))&lt;/code&gt;. There are some rules — bash doesn't support floating point arithmetic natively, so you'd reach for &lt;code&gt;bc&lt;/code&gt; if you need it. I won't go deep into that here since this isn't a full bash tutorial.&lt;/p&gt;

&lt;p&gt;Here's a simple BMI calculator I wrote while practicing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# script calculates user's BMI and gives a recommendation&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="c"&gt;# ---------------- read parameters ----------------&lt;/span&gt;
&lt;span class="nv"&gt;weight&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;bmi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;bc &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"scale=2; &lt;/span&gt;&lt;span class="nv"&gt;$weight&lt;/span&gt;&lt;span class="s2"&gt; / (&lt;/span&gt;&lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="s2"&gt;^2)"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ---------------- test ----------------&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;bc &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bmi&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt; 18.5"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"UNDERWEIGHT"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;bc &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bmi&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt; 25"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"normal weight"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;bc &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bmi&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt; 30"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"overweight"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"obese"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Process substitution&lt;/strong&gt;: this saves the output of a process into a temporary file-like descriptor under &lt;code&gt;/dev/fd/N&lt;/code&gt;. It lets you feed command output into programs that expect a file as input. Done with &lt;code&gt;&amp;gt;(command)&lt;/code&gt; and &lt;code&gt;&amp;lt;(command)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="c"&gt;# script demonstrates process substitution&lt;/span&gt;
&lt;span class="c"&gt;# process substitution is when you want to use the output of a command&lt;/span&gt;
&lt;span class="c"&gt;# as the input to a command that needs files as parameters&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="c"&gt;# gets the difference between two texts without making temp files&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;diff &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello mr beast"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"hello mr beast."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# note: &amp;lt;(cmd) uses the output of a command as a file&lt;/span&gt;
&lt;span class="c"&gt;# &amp;gt;(cmd) takes input from a program and feeds it to the command as a file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Word splitting and globbing&lt;/strong&gt;: occurs after expansion, only on unquoted values.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next up: regex, grep/sed/awk, and moving into building actual projects.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>automation</category>
      <category>fedora</category>
    </item>
    <item>
      <title>A month with bash</title>
      <dc:creator>Kengah Ireneaus</dc:creator>
      <pubDate>Mon, 03 Aug 2026 14:20:00 +0000</pubDate>
      <link>https://dev.to/4kit1-glitch/a-month-with-bash-490g</link>
      <guid>https://dev.to/4kit1-glitch/a-month-with-bash-490g</guid>
      <description>&lt;h2&gt;
  
  
  A Month With Bash
&lt;/h2&gt;

&lt;p&gt;*this began as one long write-up but i decided to split it into three parts &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.tolink"&gt;Part 1: Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/4kit1-glitch/a-month-with-bash-part-2-expansions-3277"&gt;Part 2: Going deeper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/4kit1-glitch/a-month-with-bash-part-3-building-projects-3od9"&gt;Part 3: Project building&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A month ago I decided to learn bash so I could use my Fedora machine more effectively. My original plan was to just learn some commands and get over with it, but as I began reading resources and books, I quickly noticed how important it is for Linux users, automation experts, and system admins. The thing that amazed me most is that I could literally automate almost every repetitive process I do on my PC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Starting off with bash wasn't really bad or difficult, maybe because I already had experience programming in C and Python. Learning the basic syntax of bash wasn't much of a problem — things like variables and printing to the terminal were quite easy to grasp. I also learned some theory on shell types and their differences, which wasn't too hard either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create, assign, and print a variable&lt;/span&gt;
&lt;span class="nv"&gt;my_var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"declared"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$my_var&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Moving Deeper Into Bash
&lt;/h2&gt;

&lt;p&gt;After the basics, where bash just seemed like a small programming language, things changed once I started learning about the bash environment. This is what began to show me what makes it special and unique. I learned about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shell initialization scripts

&lt;ul&gt;
&lt;li&gt;System-wide&lt;/li&gt;
&lt;li&gt;Individual user scripts&lt;/li&gt;
&lt;li&gt;How to configure them&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honestly, these files affect system behavior, so it's better to avoid touching them unless you know what you're doing. This part was quite confusing at first, but with practice it became clearer. I still don't fully understand every use case of these initialization scripts off-hand, and explaining them isn't the goal of this post.&lt;/p&gt;

&lt;p&gt;After that I took some time to learn about reserved variables — just a curated table of them. They're very useful because they enable some handy functionality, and they're good to use when making a script portable. Some important ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HOME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IFS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PWD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PATH&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DIRSTACK&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;location&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# this stores the location of the home directory&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$location&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Next up: special parameters, expansions, and how bash actually executes a script line by line.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
