<?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>Forem: DanLin</title>
    <description>The latest articles on Forem by DanLin (@danlinx2004x).</description>
    <link>https://forem.com/danlinx2004x</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3593401%2F8e77617c-e971-4db2-afe1-eb45c8872037.jpeg</url>
      <title>Forem: DanLin</title>
      <link>https://forem.com/danlinx2004x</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/danlinx2004x"/>
    <language>en</language>
    <item>
      <title>How I Got Lost in Three Logs — and Still Found the User</title>
      <dc:creator>DanLin</dc:creator>
      <pubDate>Mon, 01 Dec 2025 13:26:44 +0000</pubDate>
      <link>https://forem.com/danlinx2004x/how-i-got-lost-in-three-logs-and-still-found-the-user-1bbn</link>
      <guid>https://forem.com/danlinx2004x/how-i-got-lost-in-three-logs-and-still-found-the-user-1bbn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;del&gt;... or why last, lastlog, and journalctl behave like three different Linux&lt;/del&gt;&lt;br&gt;
&lt;del&gt;... and why writing a simple script turned into a small investigation of Linux&lt;/del&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧩 Prologue — I thought it would be easy
&lt;/h2&gt;

&lt;p&gt;This time, I wanted to write a script that could determine &lt;strong&gt;who last logged into Linux and when&lt;/strong&gt;, and then generate a report in CSV file format.&lt;br&gt;
Now... I understand why SRE engineers are gray-haired.&lt;/p&gt;

&lt;p&gt;In theory, it's clear:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;last&lt;/td&gt;
&lt;td&gt;login history (wtmp)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lastlog&lt;/td&gt;
&lt;td&gt;only the most recent entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;journalctl&lt;/td&gt;
&lt;td&gt;systemd login logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;But in practice — they behaved like three half-blind witnesses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each knows &lt;em&gt;something&lt;/em&gt;,&lt;/li&gt;
&lt;li&gt;each lies about &lt;em&gt;something&lt;/em&gt;,&lt;/li&gt;
&lt;li&gt;and sometimes… they just remain silent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  😯 Why &lt;code&gt;last&lt;/code&gt; isn't always telling the truth
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;last &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👎 still logged in — even if the user already logged out&lt;br&gt;
👎 if wtmp is rotated → history is gone&lt;br&gt;
👎 SSH/LDAP logins may not be detected&lt;br&gt;
👎 PAM config affects results — different distros show different output&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;last doesn't guarantee that it knows the truth at all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🤥  lastlog: seems reliable… until it isn’t
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lastlog &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Real user&lt;/th&gt;
&lt;th&gt;lastlog&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Logged in 3 days ago&lt;/td&gt;
&lt;td&gt;(Never logged in)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Never logged in&lt;/td&gt;
&lt;td&gt;(Never logged in)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And how can you tell which one is real?&lt;br&gt;
No way. Code fallback!&lt;/p&gt;




&lt;h2&gt;
  
  
  🛰 journalctl: almost a hero... if you're lucky
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nv"&gt;_UID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1 month ago"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;What's happening&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Various distributions&lt;/td&gt;
&lt;td&gt;systemd not available → useless command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log rotation&lt;/td&gt;
&lt;td&gt;history disappears&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sudo required&lt;/td&gt;
&lt;td&gt;some logs are hidden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Different PAM&lt;/td&gt;
&lt;td&gt;can produce completely different events&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;journalctl sometimes knows everything...&lt;br&gt;
...and sometimes it's as if it has never seen this user before.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧪 Engineering solution: fallback logic
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;last:
    &lt;span class="c"&gt;#use that date&lt;/span&gt;
&lt;span class="k"&gt;else if &lt;/span&gt;lastlog:
    &lt;span class="c"&gt;#use its data&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;:
    &lt;span class="c"&gt;#return  “never” or “invalid”&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not “beautiful code.”&lt;br&gt;
This is behavior in chaos.&lt;br&gt;
This is already “little SRE.”&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠 Script (user-last-login-tool)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;last → if there is a date — great&lt;/li&gt;
&lt;li&gt;if not → lastlog&lt;/li&gt;
&lt;li&gt;If still empty → return "never" or "invalid"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;🔧 What I added:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;set -euo pipefail&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;--help&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;--version&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CSV-report&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;logging to /var/log/sre-tools&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fallback logic for reliability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;packaged for  AUR / DEB / RPM&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dockerfile&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GitHub Actions + Makefile CI&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;man-page&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧭 What surprised me the most
&lt;/h2&gt;

&lt;p&gt;I expected to write a simple Bash script.&lt;br&gt;
Instead, I discovered that Linux has no single “source of truth” about user logins.&lt;/p&gt;

&lt;p&gt;That raised an interesting question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do SREs make decisions&lt;br&gt;
when data is incomplete or contradictory?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That question turned this script&lt;br&gt;
from a util — into an investigation.&lt;/p&gt;




&lt;h2&gt;
  
  
  👉 My project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/DanLinX2004X/user-last-login-tool" rel="noopener noreferrer"&gt;https://github.com/DanLinX2004X/user-last-login-tool&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Why I’m Sharing This
&lt;/h2&gt;

&lt;p&gt;LLMs can write code.&lt;br&gt;
But they don’t feel the pain&lt;br&gt;
when lastlog returns (Never logged in)&lt;br&gt;
for a user who logged in yesterday.&lt;/p&gt;

&lt;p&gt;That “pain” — &lt;strong&gt;is engineering experience.&lt;/strong&gt;&lt;br&gt;
And that can’t (yet) be generated.&lt;/p&gt;




&lt;h2&gt;
  
  
  🗣 Your Turn
&lt;/h2&gt;

&lt;p&gt;Have you seen different behavior of these commands in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debian?&lt;/li&gt;
&lt;li&gt;CentOS / RHEL?&lt;/li&gt;
&lt;li&gt;LDAP / SSSD setups?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If yes — I’d really love to hear about it.&lt;br&gt;
It might help make the tool actually useful.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this a universal Linux problem — or just my personal adventure?&lt;br&gt;
Let me know 👇&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>bash</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Learned IaC Without Losing My Mind 🤯</title>
      <dc:creator>DanLin</dc:creator>
      <pubDate>Sun, 09 Nov 2025 12:11:17 +0000</pubDate>
      <link>https://forem.com/danlinx2004x/how-i-learned-iac-without-losing-my-mind-347c</link>
      <guid>https://forem.com/danlinx2004x/how-i-learned-iac-without-losing-my-mind-347c</guid>
      <description>&lt;h2&gt;
  
  
  Prologue: The Quest for IaC Experience
&lt;/h2&gt;

&lt;p&gt;I decided to prepare for an SRE/DevOps internship and learn Infrastructure as Code.&lt;br&gt;&lt;br&gt;
The challenge: build a portfolio project demonstrating IaC skills, but &lt;strong&gt;without using clouds or paid VPS&lt;/strong&gt; — student budget is tight, and I don't trust those “free trials” that suddenly turn into bills.&lt;/p&gt;

&lt;p&gt;I asked AI assistants for advice and got:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use Vagrant + Ansible + Terraform!"  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounded impressive. In practice… well, let's just say it was an adventure.  &lt;/p&gt;


&lt;h2&gt;
  
  
  Chapter 1: The Rise and Fall of Vagrant
&lt;/h2&gt;

&lt;p&gt;Installed Vagrant on my CachyOS and added plugins as recommended by Arch Wiki.&lt;br&gt;&lt;br&gt;
Created a basic Vagrantfile — what could possibly go wrong?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error #1: The Ghost of Plugins Past&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;undefined&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="s1"&gt;'exists?'&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NoMethodError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists?&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;vagrant-vbguest&lt;/code&gt; plugin had been abandoned since 2023 and was incompatible with modern Ruby.&lt;br&gt;
The solution was surprisingly simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vbguest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;auto_update&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After some ritual dancing, the VM finally booted! 🥳&lt;/p&gt;




&lt;h2&gt;
  
  
  Chapter 2: Terraform and the Over-engineering Trap
&lt;/h2&gt;

&lt;p&gt;Next phase: two VMs via Terraform + Ansible configuration.&lt;br&gt;
This is where the &lt;strong&gt;real fun&lt;/strong&gt; began:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ansible installed Docker, Prometheus, and Grafana on both VMs — why?&lt;/li&gt;
&lt;li&gt;Docker-ce refused to install due to dependency hell&lt;/li&gt;
&lt;li&gt;At some point Ansible just froze without explanation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most ironic moment: after hours of struggle, the AI assistant finally declared:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Vagrant is legacy crap, just drop it"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I felt like a character in Harlan Ellison's &lt;em&gt;“I Have No Mouth, and I Must Scream”&lt;/em&gt; — trapped in a digital nightmare of my own making. 🤡&lt;/p&gt;


&lt;h2&gt;
  
  
  Chapter 3: Monitron and the Golden Screwdriver
&lt;/h2&gt;

&lt;p&gt;I decided to improve my existing &lt;strong&gt;Monitron project&lt;/strong&gt; (a monitoring stack in Docker Compose) by adding Ansible.&lt;br&gt;
The AI insisted: “This will give you idempotency and portability!”&lt;/p&gt;

&lt;p&gt;In practice, I got a playbook that… &lt;strong&gt;copied files and ran &lt;code&gt;docker-compose up&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;
Ansible became a golden screwdriver for a simple task.&lt;/p&gt;


&lt;h2&gt;
  
  
  Chapter 4: The PostgreSQL Wars
&lt;/h2&gt;

&lt;p&gt;In my final attempt to create &lt;strong&gt;ansible-lab&lt;/strong&gt;, I faced epic PostgreSQL battles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission Error&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;chmod&lt;/span&gt;: invalid mode: &lt;span class="s1"&gt;'A+user:postgres:rx:allow'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Authentication Nightmare&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;FATAL: Peer authentication failed &lt;span class="k"&gt;for &lt;/span&gt;user &lt;span class="s2"&gt;"postgres"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI suggested everything — installing extra collections, solving non-existent permission issues, and even thought I was on Windows!&lt;/p&gt;

&lt;p&gt;The solution turned out to be laughably simple — replace complex modules with shell commands:&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="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;Create PostgreSQL user&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo -u postgres psql -c "CREATE USER myapp_user WITH PASSWORD 'secret';"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And miracle of miracles — it worked on the first try!&lt;/p&gt;




&lt;h2&gt;
  
  
  Epilogue: ansible-lab and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;I finally built a working pet project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vagrant creates two VMs (&lt;code&gt;db&lt;/code&gt; + &lt;code&gt;app&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ansible manages them via roles&lt;/li&gt;
&lt;li&gt;No Terraform, no unnecessary complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lessons Learned:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Trust, but Verify&lt;/strong&gt;
AI assistants are tools, not gurus. Always ask:&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;"Why do I need this technology for my specific use case?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ansible Without VMs is Over-engineering&lt;/strong&gt;&lt;br&gt;
Use Bash for your local machine, Docker for containers.&lt;br&gt;
Ansible shines when you have multiple servers to manage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sometimes Simple is Better&lt;/strong&gt;&lt;br&gt;
My final solution was simpler than the original plans, but it &lt;strong&gt;actually works&lt;/strong&gt; and demonstrates exactly what I needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real Experience &amp;gt; Perfect Tutorials&lt;/strong&gt;&lt;br&gt;
In interviews, real problem-solving stories are valued more than memorized syntax.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;💡 &lt;strong&gt;Advice for Beginners:&lt;/strong&gt;&lt;br&gt;
Start with small but working projects. Sometimes a simple Vagrant lab is more valuable than a complex "everything-but-the-kitchen-sink" setup.&lt;/p&gt;




&lt;p&gt;&lt;del&gt;P.S. Dear AI assistants, I still love you. But sometimes you're absolute trolls. 😄&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/DanLinX2004X/ansible-lab" rel="noopener noreferrer"&gt;Check out my ansible-lab on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
      <category>automation</category>
      <category>ai</category>
    </item>
    <item>
      <title>💀 I built a full system monitor in Bash — and fought awk along the way</title>
      <dc:creator>DanLin</dc:creator>
      <pubDate>Tue, 04 Nov 2025 21:16:25 +0000</pubDate>
      <link>https://forem.com/danlinx2004x/i-built-a-full-system-monitor-in-bash-and-fought-awk-along-the-way-53ge</link>
      <guid>https://forem.com/danlinx2004x/i-built-a-full-system-monitor-in-bash-and-fought-awk-along-the-way-53ge</guid>
      <description>&lt;p&gt;I wanted to see how far I could push pure Bash before it collapses under its own syntax.&lt;br&gt;
So, naturally, I decided to write a system monitoring tool — in Bash.&lt;br&gt;
And thus, system-monitor was born:&lt;br&gt;
👉 &lt;a href="https://github.com/DanLinX2004X/system-monitor" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Yes, it’s fully functional. Yes, it uses colors. Yes, awk is involved.&lt;br&gt;&lt;br&gt;
No, I don’t recommend doing this sober. 😅  &lt;/p&gt;


&lt;h2&gt;
  
  
  💡 The idea
&lt;/h2&gt;

&lt;p&gt;I wanted a single script that could show:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU load and number of cores
&lt;/li&gt;
&lt;li&gt;RAM usage and percentage
&lt;/li&gt;
&lt;li&gt;Disk space for /
&lt;/li&gt;
&lt;li&gt;Network I/O
&lt;/li&gt;
&lt;li&gt;Process count
Basically, the “lazy Linux admin toolkit” in one file.
Something like this:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./system-monitor.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and boom — everything you’d usually get from top, free, df, and ip combined.&lt;/p&gt;



&lt;p&gt;⚙️ The features&lt;/p&gt;

&lt;p&gt;What started as a 10-line script turned into a 250+ line CLI tool with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Command-line arguments (--help, --brief, --no-color, --version, -i N)&lt;/li&gt;
&lt;li&gt;Colorized output for warnings/critical thresholds&lt;/li&gt;
&lt;li&gt;Continuous monitoring mode&lt;/li&gt;
&lt;li&gt;Safe shutdown with signal trapping (Ctrl+C)&lt;/li&gt;
&lt;li&gt;Brief (machine-readable) mode for piping into other scripts&lt;/li&gt;
&lt;li&gt;Dependency checks and graceful error handling&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./system-monitor.sh &lt;span class="nt"&gt;-i&lt;/span&gt; 5 &lt;span class="nt"&gt;--no-color&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Updates every 5 seconds without colors.&lt;br&gt;
Press Ctrl+C to stop — yes, it even says goodbye politely.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;🧩 Some code highlights&lt;/p&gt;

&lt;p&gt;Color management:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because monitoring your system is serious business — but who doesn’t love a bit of RGB?&lt;/p&gt;

&lt;p&gt;Load detection magic:&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;CPU_LOAD_1MIN&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; /proc/loadavg | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CPU_CORES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;load_percent&lt;/span&gt;&lt;span class="o"&gt;=&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;"scale=0; (&lt;/span&gt;&lt;span class="nv"&gt;$CPU_LOAD_1MIN&lt;/span&gt;&lt;span class="s2"&gt; * 100) / &lt;/span&gt;&lt;span class="nv"&gt;$CPU_CORES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That moment when you realize you’re using awk, bc, and /proc in one line and it actually works.&lt;/p&gt;

&lt;p&gt;Network metrics:&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;interface&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ip route get 8.8.8.8 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $5}'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;rx&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; /sys/class/net/&lt;span class="nv"&gt;$interface&lt;/span&gt;/statistics/rx_bytes&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;tx&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; /sys/class/net/&lt;span class="nv"&gt;$interface&lt;/span&gt;/statistics/tx_bytes&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like to imagine Bash crying softly every time it runs this.&lt;/p&gt;




&lt;p&gt;🧠 What I learned&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash can do a lot — if you’re patient (and slightly masochistic).&lt;/li&gt;
&lt;li&gt;Quoting is a survival skill. Forget one and you’re debugging for hours.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;awk&lt;/code&gt; is an ancient curse. It always works, but never for the reason you expect.&lt;/li&gt;
&lt;li&gt;Color helps debugging. Visual feedback saves your sanity.&lt;/li&gt;
&lt;li&gt;Document your code. Because 3 a.m. you will have no clue what that &lt;code&gt;$7&lt;/code&gt; in &lt;code&gt;awk '{print $7}'&lt;/code&gt; was.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🧰 The brief mode&lt;/p&gt;

&lt;p&gt;I added a &lt;code&gt;--brief&lt;/code&gt; flag that outputs all metrics in a machine-readable format — perfect for logging or Prometheus-style integration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./system-monitor.sh &lt;span class="nt"&gt;--brief&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timestamp=1730765432
cpu_load_percent=12
mem_usage_percent=43.5
disk_usage_percent=56
network_rx_mb=12.4
process_count=187
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, JSON for people who hate themselves.&lt;/p&gt;




&lt;p&gt;🧯 Safety first: signal handling&lt;/p&gt;

&lt;p&gt;If you hit Ctrl+C, it doesn’t just die — it politely says goodbye:&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;trap &lt;/span&gt;cleanup INT TERM

cleanup&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="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Monitoring stopped. Goodbye!"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because professionalism.&lt;/p&gt;




&lt;p&gt;🚀 AUR packaging&lt;/p&gt;

&lt;p&gt;After testing it on Arch, I thought — why not go all in?&lt;br&gt;
So I wrote a PKGBUILD, threw it into the AUR, and now you can literally install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; system-monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s the moment I realized:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Oh no. I’ve become that guy who writes Bash utilities for other people.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;🧭 Final thoughts&lt;/p&gt;

&lt;p&gt;Was this efficient? Probably not.&lt;br&gt;
Did I learn a lot? Absolutely.&lt;/p&gt;

&lt;p&gt;Here’s what I got from it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deep understanding of Bash syntax and traps&lt;/li&gt;
&lt;li&gt;Practical use of /proc and awk&lt;/li&gt;
&lt;li&gt;Appreciation for proper CLI design&lt;/li&gt;
&lt;li&gt;And a newfound respect for people who don’t do this in Bash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re thinking of writing your first utility — do it.&lt;br&gt;
Even if it’s messy. Even if awk haunts your dreams.&lt;/p&gt;

&lt;p&gt;Because once you see your tool working, it’s worth every headache.&lt;/p&gt;




&lt;p&gt;🔗 Links&lt;/p&gt;

&lt;p&gt;🐧 GitHub: &lt;a href="https://github.com/DanLinX2004X/system-monitor" rel="noopener noreferrer"&gt;https://github.com/DanLinX2004X/system-monitor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📦 AUR: &lt;a href="https://aur.archlinux.org/packages/system-monitor" rel="noopener noreferrer"&gt;https://aur.archlinux.org/packages/system-monitor&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>devops</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
