<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged scripting</title>
<link>https://coderlegion.com/tag/scripting</link>
<description>Powered by Question2Answer</description>
<item>
<title>How to Explain a Process with an Animation Explainer</title>
<link>https://coderlegion.com/32479/how-to-explain-a-process-with-an-animation-explainer</link>
<description>&lt;p&gt;An animation explainer is a short moving picture that makes one idea clear. When the idea is a process, a before and after can carry the whole point: show the starting state, then the ending state, and let the viewer feel the change. Use this shape when the start and end are easy to picture and the middle is short. If the middle is long or branching, show the steps instead.&lt;/p&gt;
&lt;h3 id=&quot;what-matters-when-you-choose-an-animation-explainer-&quot;&gt;What matters when you choose an animation explainer?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The starting state and the ending state are both easy to picture.&lt;/li&gt;
&lt;li&gt;The change solves a problem the viewer already feels.&lt;/li&gt;
&lt;li&gt;The middle fits in a few visible moves.&lt;/li&gt;
&lt;li&gt;The viewer can hold both states in mind on one pass.&lt;/li&gt;
&lt;li&gt;The ending state removes a real cost, not just tidies it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If two or three of these are weak, the format is probably wrong. That is not a failure of the idea; it is a sign to choose a different shape.&lt;/p&gt;
&lt;p&gt;The decision is not about style; it is about what the viewer needs first. If the change is the news, lead with the change. If the steps are the news, lead with the steps and let the ending state arrive last.&lt;/p&gt;
&lt;h3 id=&quot;how-do-you-judge-each-criterion-&quot;&gt;How do you judge each criterion?&lt;/h3&gt;
&lt;p&gt;Start with the two pictures, not the steps. If you cannot sketch the before and after in a few seconds, the viewer will not hold them either. A messy shared drive and a tidy folder system works because both are visible, while &quot;better data hygiene&quot; is not. If the pictures need the words to make sense, the pictures are not doing the work.&lt;/p&gt;
&lt;p&gt;The change has to solve a problem the viewer already feels. If the after state only saves time for the team that runs the process, the before and after will feel like an internal memo dressed up as a story. Tie the ending state to something the viewer loses today: lost files, repeated questions, a task that takes too long, or a handoff that fails. The before picture should make that loss visible in one glance.&lt;/p&gt;
&lt;p&gt;Count the moves between the two pictures, not the minutes. If the middle needs more than a handful of actions, the before and after will skip the part that actually matters. In the shared drive example, rename, sort, and remove duplicates is short; adding permissions, naming rules, and training is not. When the middle grows, switch to a step-by-step explainer video and keep the before and after as the first and last frames.&lt;/p&gt;
&lt;p&gt;The viewer should see both states without scrolling back or replaying. That means one screen, or one clear transition, not a long tour of details that only make sense in order. If you need a second viewing to compare, the change is too large for this format. A single animated video can still show detail, but only after the main change is locked in and the viewer knows what to look for.&lt;/p&gt;
&lt;p&gt;The after state must look like relief, not like a different kind of work. If the new process only moves the pain somewhere else, the before and after will read as a trade, not a win. Name the cost that disappears, and make sure it disappears in the picture, not only in the voiceover. Show the moment the old cost stops, and let the new state stay on screen long enough to feel earned.&lt;/p&gt;
&lt;p&gt;The trade-off is simple: clarity now versus completeness later. A before and after buys attention and care; it does not buy a full procedure. If you need the full procedure, plan a second piece.&lt;/p&gt;
&lt;p&gt;When you judge the criteria, judge them in this order: pictures first, then pain, then middle, then memory, then relief. If the first fails, stop; the rest do not matter. This order saves time and protects the idea.&lt;/p&gt;
&lt;h3 id=&quot;which-red-flags-show-it-is-the-wrong-choice-&quot;&gt;Which red flags show it is the wrong choice?&lt;/h3&gt;
&lt;p&gt;Some processes fight this format. The signs usually show up while you are still planning, before anyone draws a frame or writes a line. If two or more of these are true, plan a step-by-step explainer video instead and keep the before and after for a single closing shot.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The before and after look almost the same on screen.&lt;/li&gt;
&lt;li&gt;The process has branches, exceptions, or approvals that carry the real point.&lt;/li&gt;
&lt;li&gt;The viewer needs numbers, labels, or a legend to see the change.&lt;/li&gt;
&lt;li&gt;The after state needs a long explanation of why it is better.&lt;/li&gt;
&lt;li&gt;The middle contains a decision that changes the outcome.&lt;/li&gt;
&lt;li&gt;The same process looks different for different people.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are not small flaws; they are format mismatches. A before and after in an animation explainer works when the change is the story and the steps are short enough to trust. When the steps or the rules are the story, show them in order and let the ending state arrive last. The viewer will still feel the improvement, but they will also understand how to reach it.&lt;/p&gt;
&lt;p&gt;Do not force a process into this shape because the pictures look good. The format is a promise that the change is visible and short. Break that promise and the viewer stops trusting the next animation explainer you make.&lt;/p&gt;
&lt;p&gt;If only one red flag is true, you may still use the format, but name the limit in the script. Honesty about the limit keeps the piece useful. Hiding it turns a small mismatch into a broken promise.&lt;/p&gt;
&lt;h3 id=&quot;what-should-you-ask-before-you-commit-&quot;&gt;What should you ask before you commit?&lt;/h3&gt;
&lt;p&gt;These questions force the change into words before it becomes pictures. If an answer stays vague, the animation will stay vague too.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What exactly changes between the first frame and the last frame?&lt;/li&gt;
&lt;li&gt;Can a newcomer name that change in one sentence?&lt;/li&gt;
&lt;li&gt;Which step in the middle carries the risk, cost, or delay?&lt;/li&gt;
&lt;li&gt;What would make the after state feel unearned?&lt;/li&gt;
&lt;li&gt;How will you show the change with pictures before you add words?&lt;/li&gt;
&lt;li&gt;Who in the audience will not feel the problem in the before state?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the last question has no clear answer, the before picture is probably too generic. Fix the before state first; the after state depends on it.&lt;/p&gt;
&lt;p&gt;Answer them in plain words before you sketch. If an answer needs a diagram to make sense, the picture is not ready yet. The questions are a filter, not a script.&lt;/p&gt;
&lt;h3 id=&quot;frequently-asked-questions&quot;&gt;Frequently asked questions&lt;/h3&gt;
&lt;h4 id=&quot;can-a-before-and-after-replace-the-whole-process-&quot;&gt;Can a before and after replace the whole process?&lt;/h4&gt;
&lt;p&gt;It can replace the overview, not the training. Use it to make someone care about the change, then use a step-by-step format for the actions. If you try to teach every step inside the before and after, the middle will blur and the ending will feel unearned. The viewer remembers the feeling of the change, not the procedure.&lt;/p&gt;
&lt;p&gt;A good handoff is to end with one clear next step and a place to find the full steps. That keeps the animation explainer focused on the change while still respecting the viewer who wants to act. The feeling comes first; the procedure follows.&lt;/p&gt;
&lt;h4 id=&quot;what-if-the-process-has-more-than-one-good-outcome-&quot;&gt;What if the process has more than one good outcome?&lt;/h4&gt;
&lt;p&gt;Pick the outcome that removes the pain the viewer already knows. Showing two or three endings turns the piece into a comparison chart, which is a different job with different pacing. If the outcomes depend on context, say so in one line and keep the main change simple. You can always end with a short note that other paths exist.&lt;/p&gt;
&lt;p&gt;When context matters, name the condition in the voiceover and keep the picture on the main case. The viewer can hold one exception in mind if it is stated plainly. Two exceptions usually need a different format.&lt;/p&gt;
&lt;h4 id=&quot;how-long-should-the-middle-part-be-&quot;&gt;How long should the middle part be?&lt;/h4&gt;
&lt;p&gt;Short enough that the viewer does not lose the thread. If you need to pause, rewind, or add a label to keep up, the middle is too long for this shape. Cut the middle to the moves that change the picture, and move the rest into a later section or a separate explainer video script. The middle is not the place to be complete; it is the place to be believable.&lt;/p&gt;
&lt;p&gt;A believable middle has fewer moves than a complete one. When in doubt, cut the middle by half and see if the change still makes sense. Most middles survive the cut better than their authors expect.&lt;/p&gt;
&lt;p&gt;Try sketching the before and after on paper before you write a script. If both pictures are clear and the middle fits in a few moves, you have a strong shape for an animation explainer. If not, switch to steps and save the before and after for the final frame.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32479/how-to-explain-a-process-with-an-animation-explainer</guid>
<pubDate>Mon, 26 Oct 2026 09:17:49 +0000</pubDate>
</item>
<item>
<title>My ERR trap stayed silent until I added one letter to set -euo pipefail</title>
<link>https://coderlegion.com/28017/my-err-trap-stayed-silent-until-i-added-one-letter-to-set-euo-pipefail</link>
<description>&lt;p&gt;A nine-line script, &lt;code&gt;set -euo pipefail&lt;/code&gt; on line 2 and an ERR trap on line 3 whose only job is to print the failing command and its line number. A helper function then runs &lt;code&gt;mkdir /proc/export&lt;/code&gt;, a directory that cannot be created. On bash 5.3.9 it printed mkdir's own complaint and exited 1. The trap said nothing: no command, no line, the two facts it was registered to report.&lt;/p&gt;
&lt;p&gt;By default the ERR trap is not inherited by functions, command substitutions or subshells. Errexit killed the script from inside the function; the trap, registered at the top level, never saw the failure. One letter fixes it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;set -Eeuo pipefail
trap 'echo &quot;✗ failed: ${BASH_COMMAND} (line ${LINENO})&quot; &amp;gt;&amp;amp;2' ERR
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-E&lt;/code&gt; (errtrace) makes the trap follow the script into functions. Same run, same mkdir error, now followed by &lt;code&gt;✗ failed: mkdir /proc/export (line 5)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two more silent spots from the same session. &lt;code&gt;local out=$(false)&lt;/code&gt; inside a function carried on to the next line and the script exited 0, because &lt;code&gt;local&lt;/code&gt; succeeded and its status is the one bash checks. Declare on one line, assign on the next. And in an EXIT handler, capture &lt;code&gt;$?&lt;/code&gt; on the first line: moved below an &lt;code&gt;rm -f&lt;/code&gt;, it reports rm's zero, and a script ending in &lt;code&gt;exit 3&lt;/code&gt; exits 0.&lt;/p&gt;
&lt;p&gt;Where errexit goes silent, &lt;code&gt;nounset&lt;/code&gt; defaults and the assembled skeleton with both traps are in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/guides/safe-bash-script-template&quot;&gt;strict-mode bash script template, checked against bash 5.3&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28017/my-err-trap-stayed-silent-until-i-added-one-letter-to-set-euo-pipefail</guid>
<pubDate>Tue, 22 Sep 2026 13:00:00 +0000</pubDate>
</item>
<item>
<title>The Alert Never Fired Because the Loop Skipped the Last Line of the File</title>
<link>https://coderlegion.com/20988/the-alert-never-fired-because-the-loop-skipped-the-last-line-of-the-file</link>
<description>&lt;p&gt;We kept a plaintext file of hostnames, one per line, and a monitoring script read the file and pinged each host every five minutes. When a host failed to respond, the script sent an email alert. The system had been running for months and it worked — we had caught three actual outages with it, which gave us real confidence in the setup.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app-07&lt;/code&gt; was added to the list on a Thursday afternoon. The engineer who added it was using VS Code on a Mac, and VS Code by default does not add a trailing newline to a file when you append to it using certain editing workflows. The file had ended in a newline before the edit. After the edit, the last line — &lt;code&gt;app-07&lt;/code&gt; — had no trailing newline.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app-07&lt;/code&gt; went down the following Sunday afternoon at 2:17pm. The monitoring script ran at 2:20, 2:25, 2:30, all the way through the evening. No alert ever fired. The on-call engineer found out at 8pm when a client emailed. The system had been down for almost six hours.&lt;/p&gt;
&lt;p&gt;When I looked at the script, the bug was immediately obvious once I knew what to look for. But I had written that script, I had tested it, and I had been looking at the monitoring confirmation emails for months without ever noticing. The confirmation email listed the hosts it checked. &lt;code&gt;app-07&lt;/code&gt; was never in the list. I had been reading those emails without actually counting the hosts. I just scanned for the OK lines and moved on.&lt;/p&gt;
&lt;h3 id=&quot;why-read-drops-the-last-line&quot;&gt;Why read drops the last line&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;read&lt;/code&gt; returns a success exit status when it reads a line and finds the newline that terminates it. When the file does not end in a newline, &lt;code&gt;read&lt;/code&gt; still populates the variable with the final line's content, but it returns a non-zero (failure) exit status because it hit end-of-file before finding a terminator. A &lt;code&gt;while read host&lt;/code&gt; loop checks the return status to decide whether to execute the loop body. On the final, newline-less line, &lt;code&gt;read&lt;/code&gt; puts &lt;code&gt;app-07&lt;/code&gt; into &lt;code&gt;host&lt;/code&gt; and then returns failure. The &lt;code&gt;while&lt;/code&gt; loop sees failure and exits without running the body. The content is there. The variable is populated. The loop throws it away.&lt;/p&gt;
&lt;p&gt;This behavior is documented in the POSIX spec for &lt;code&gt;read&lt;/code&gt;. It is not a bash quirk. Any POSIX shell handles the missing-final-newline case this way. A plain &lt;code&gt;while read line&lt;/code&gt; loop is incorrect for any file you do not personally control the formatting of, which in practice means nearly any file.&lt;/p&gt;
&lt;p&gt;The fix is one extra clause:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash

# Script: check-hosts.sh

# Purpose: ping every host in a file, including a newline-less final line

# Usage: ./check-hosts.sh hosts.txt

set -euo pipefail

CHECK=&quot;✓&quot;
CROSS=&quot;✗&quot;
HOST_FILE=&quot;${1:?Usage: check-hosts.sh &amp;lt;host-file&amp;gt;}&quot;

while IFS= read -r host || [[ -n &quot;$host&quot; ]]; do
  [[ -z &quot;$host&quot; || &quot;$host&quot; == \#* ]] &amp;amp;&amp;amp; continue
  if ping -c1 -W2 &quot;$host&quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then
    echo &quot;$CHECK up:   $host&quot;
  else
    echo &quot;$CROSS down: $host&quot;
  fi
done &amp;lt; &quot;$HOST_FILE&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;|| [[ -n &quot;$host&quot; ]]&lt;/code&gt; says: if &lt;code&gt;read&lt;/code&gt; returned failure but the variable is non-empty, run the loop body anyway. That is precisely the leftover-final-line case. &lt;code&gt;read&lt;/code&gt; failed because it hit end-of-file, but it populated &lt;code&gt;host&lt;/code&gt; with &lt;code&gt;app-07&lt;/code&gt; before returning. The &lt;code&gt;||&lt;/code&gt; catches it. &lt;code&gt;app-07&lt;/code&gt; gets pinged.&lt;/p&gt;
&lt;h3 id=&quot;what-ifs-and--r-actually-do&quot;&gt;What IFS= and -r actually do&lt;/h3&gt;
&lt;p&gt;You see &lt;code&gt;while IFS= read -r line&lt;/code&gt; written in every correct read-loop example, and it is worth being specific about what each piece prevents because both have their own failure mode.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;IFS=&lt;/code&gt; sets the field separator to empty for the duration of the &lt;code&gt;read&lt;/code&gt; command. Without it, &lt;code&gt;read&lt;/code&gt; strips leading and trailing whitespace from each line. A hostname like &lt;code&gt;  app-07  &lt;/code&gt; (with leading spaces, which some editors produce) becomes &lt;code&gt;app-07&lt;/code&gt;, which might be correct. An indented config value, a Python-style YAML string, a log line that starts with spaces for alignment — all of these are silently modified. Setting &lt;code&gt;IFS=&lt;/code&gt; tells &lt;code&gt;read&lt;/code&gt; to take the line exactly as it appears.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-r&lt;/code&gt; prevents &lt;code&gt;read&lt;/code&gt; from interpreting backslash sequences. Without &lt;code&gt;-r&lt;/code&gt;, a line like &lt;code&gt;C:\temp\logs&lt;/code&gt; has its backslashes consumed as escape characters and arrives as &lt;code&gt;C:templogs&lt;/code&gt;. This matters less for hostname files and enormously for any script that processes Windows paths, config files that use backslash as a line-continuation character, or log files from mixed-OS environments. The &lt;code&gt;-r&lt;/code&gt; flag is essentially free protection; there is no reason not to include it.&lt;/p&gt;
&lt;p&gt;A bare &lt;code&gt;read line&lt;/code&gt; without either flag silently mangles both whitespace and backslashes. The script works correctly on clean input and produces wrong output on input with edge cases. The wrong output does not produce an error. You find out when the data that mattered was the indented or backslash-containing kind.&lt;/p&gt;
&lt;h3 id=&quot;the-subshell-trap-that-kills-your-counters&quot;&gt;The subshell trap that kills your counters&lt;/h3&gt;
&lt;p&gt;This is the one that is most likely to make you question your sanity:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;
# This looks correct. It is not.

fails=0
cat hosts.txt | while IFS= read -r host; do
  ping -c1 -W2 &quot;$host&quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || ((fails++))
done
echo &quot;Total failures: $fails&quot;   # Always prints 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pipe creates a subshell for the right side. The &lt;code&gt;while&lt;/code&gt; loop runs inside that subshell. &lt;code&gt;fails&lt;/code&gt; increments correctly inside the subshell. When the subshell exits, the parent shell's &lt;code&gt;fails&lt;/code&gt; is still &lt;code&gt;0&lt;/code&gt;, because the increment happened in a different process. The parent echo sees the original value.&lt;/p&gt;
&lt;p&gt;This catches people because the loop body itself works — the pings happen, the increment logic is correct — but any state the loop was supposed to accumulate for later use is silently discarded. I spent forty minutes on a version of this problem before I remembered that pipes create subshells. It is the kind of thing that feels like a bash bug until you understand that it is behaving exactly as documented.&lt;/p&gt;
&lt;p&gt;The fix is to redirect the file into the loop instead of piping into it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;fails=0
while IFS= read -r host || [[ -n &quot;$host&quot; ]]; do
  ping -c1 -W2 &quot;$host&quot; &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 || ((fails++))
done &amp;lt; hosts.txt
echo &quot;Total failures: $fails&quot;   # Now correct
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;done &amp;lt; hosts.txt&lt;/code&gt; feeds the file to the loop's stdin without a pipe. The loop runs in the current shell. &lt;code&gt;fails&lt;/code&gt; accumulates in the current shell. The echo sees the real count.&lt;/p&gt;
&lt;h3 id=&quot;why-for-loop-is-wrong-for-this&quot;&gt;Why for loop is wrong for this&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;
# Never do this — iterates words, not lines

for line in $(cat hosts.txt); do
  ping -c1 &quot;$line&quot;
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;$(cat hosts.txt)&lt;/code&gt; is command substitution. Bash captures the text output and word-splits it on IFS — spaces, tabs, newlines. For a file with one hostname per line and no spaces in the hostnames, this accidentally produces the right behavior. For any file with spaces — log lines, config values, paths with spaces, anything a non-developer might have generated — it splits lines into fragments and each fragment becomes a loop iteration.&lt;/p&gt;
&lt;p&gt;There is no version of &lt;code&gt;for line in $(cat file)&lt;/code&gt; that is correct for reading lines. The right tool is always &lt;code&gt;while IFS= read -r line || [[ -n &quot;$line&quot; ]]; do ... done &amp;lt; file&lt;/code&gt;. The for loop is the right tool for iterating a known list that you control directly, not for reading file content.&lt;/p&gt;
&lt;h3 id=&quot;the-monitoring-system-after-the-fix&quot;&gt;The monitoring system, after the fix&lt;/h3&gt;
&lt;p&gt;After the &lt;code&gt;app-07&lt;/code&gt; incident we made three changes. The obvious one was fixing the read loop with the &lt;code&gt;|| [[ -n &quot;$host&quot; ]]&lt;/code&gt; guard. The second was adding a sanity check at the top of the script that counted the lines in the hosts file and compared it to the number of hosts the loop actually processed — a mismatch meant the file was malformed or something else was wrong. The third was adding a nightly email that included the count of hosts checked, not just the status of each one, so a future addition to the file that somehow got lost would show up as &quot;expected 12 hosts, checked 11.&quot;&lt;/p&gt;
&lt;p&gt;The confirmation email count was something I should have had from the start. If I had been looking at &quot;checked 11/12 hosts&quot; instead of a list of OK lines, I would have noticed &lt;code&gt;app-07&lt;/code&gt; missing on the first night. The monitoring was working. The observability of the monitoring was not.&lt;/p&gt;
&lt;p&gt;Full version with CSV-field parsing, comment-skipping, and the subshell-safe redirect form: &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/snippets/bash-read-file-line-by-line&quot;&gt;https://bashsnippets.xyz/snippets/bash-read-file-line-by-line&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To iterate a list of files rather than a file's contents, reach for a &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/snippets/bash-for-loop-examples&quot;&gt;for loop&lt;/a&gt; instead, and wrap anything that acts on what it reads in &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/snippets/bash-error-handling&quot;&gt;set -euo pipefail&lt;/a&gt;. More at &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz&quot;&gt;https://bashsnippets.xyz&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/snippets/bash-read-file-line-by-line&quot;&gt;bashsnippets.xyz/snippets/bash-read-file-line-by-line&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20988/the-alert-never-fired-because-the-loop-skipped-the-last-line-of-the-file</guid>
<pubDate>Fri, 19 Jun 2026 17:58:59 +0000</pubDate>
</item>
<item>
<title>A For Loop Skipped Every File With a Space and Called the Backup a Success</title>
<link>https://coderlegion.com/20946/a-for-loop-skipped-every-file-with-a-space-and-called-the-backup-a-success</link>
<description>&lt;p&gt;The nightly backup looped over &lt;code&gt;for f in $(ls /data/exports)&lt;/code&gt; and copied each file to a backup volume. It exited clean every night. For three weeks, green exit codes, no errors, nothing in the logs to suggest anything was wrong. The backup script had been written by someone who left the company six months before I started, and it had never been tested against files with spaces in their names because the original export directory only ever had files like &lt;code&gt;Q3.xlsx&lt;/code&gt; and &lt;code&gt;report-final.xlsx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then someone generated &lt;code&gt;Q3 final.xlsx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It took three weeks because that file was generated once a quarter and the next time someone needed it was a quarter later. The person who needed it was the CFO. The CFO does not particularly enjoy being told that the backup system that was supposed to protect the quarterly export has been silently failing for an indeterminate period of time and we are not sure which other files it missed.&lt;/p&gt;
&lt;p&gt;I know the specific backup had been running for three weeks because that was the date the file appeared in the exports directory. Every night since then, the loop had been splitting &lt;code&gt;Q3 final.xlsx&lt;/code&gt; into two items — &lt;code&gt;Q3&lt;/code&gt; and &lt;code&gt;final.xlsx&lt;/code&gt; — trying to copy two paths that did not exist, logging two harmless &quot;no such file or directory&quot; lines that nobody read, and moving on. Every file without a space in its name backed up fine. The script looked correct because most of the time it was.&lt;/p&gt;
&lt;h3 id=&quot;why-ls-word-splits&quot;&gt;Why $(ls) word-splits&lt;/h3&gt;
&lt;p&gt;When you write &lt;code&gt;for f in $(ls /data/exports)&lt;/code&gt;, bash runs &lt;code&gt;ls&lt;/code&gt;, captures its text output, and splits it on IFS — the internal field separator, which defaults to spaces, tabs, and newlines. Filenames are just text in the output of &lt;code&gt;ls&lt;/code&gt;. A file named &lt;code&gt;Q3 final.xlsx&lt;/code&gt; is one filename, but &lt;code&gt;ls&lt;/code&gt; outputs it as the string &lt;code&gt;Q3 final.xlsx&lt;/code&gt;, and bash splits that string on the space into two separate items before the loop ever starts.&lt;/p&gt;
&lt;p&gt;This is not a bug in &lt;code&gt;ls&lt;/code&gt;. This is not a bug in bash. This is exactly what &lt;code&gt;$( )&lt;/code&gt; does to any command's output — it captures text and bash processes it as text. The problem is that filenames are not reliably text-safe; they can contain any character except null and the path separator. Spaces are common. Tabs are less common but legal. Newlines are technically legal. Treating command output as a filename list breaks the moment any of those show up.&lt;/p&gt;
&lt;p&gt;The fix is to stop treating command output as a filename list and let bash build the list from the filesystem directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash
# Script: backup-exports.sh
# Purpose: copy export files without losing ones with spaces in their names
# Usage: ./backup-exports.sh
set -euo pipefail

CHECK=&quot;✓&quot;
CROSS=&quot;✗&quot;
SRC_DIR=&quot;/data/exports&quot;
DEST_DIR=&quot;/backup/exports&quot;
shopt -s nullglob   # empty glob expands to nothing, not the literal pattern

for f in &quot;$SRC_DIR&quot;/*.xlsx; do
  if cp -- &quot;$f&quot; &quot;$DEST_DIR/&quot;; then
    echo &quot;$CHECK backed up: $(basename &quot;$f&quot;)&quot;
  else
    echo &quot;$CROSS failed: $(basename &quot;$f&quot;)&quot;
  fi
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;for f in &quot;$SRC_DIR&quot;/*.xlsx&lt;/code&gt; asks bash to expand the glob. Bash talks directly to the filesystem and gets back a properly-separated list of matching paths. No command output, no text splitting, no ambiguity about what the separator is. A file named &lt;code&gt;Q3 final.xlsx&lt;/code&gt; stays one item because it was never turned into text and split back apart. The &lt;code&gt;--&lt;/code&gt; before &lt;code&gt;&quot;$f&quot;&lt;/code&gt; tells &lt;code&gt;cp&lt;/code&gt; to stop reading flags, so a filename that starts with a dash does not get interpreted as a &lt;code&gt;cp&lt;/code&gt; option.&lt;/p&gt;
&lt;h3 id=&quot;the-second-half-quoting-on-use&quot;&gt;The second half: quoting on use&lt;/h3&gt;
&lt;p&gt;The glob fixes the loop header. Quoting fixes the point of use. These are separate.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;for f in &quot;$SRC_DIR&quot;/*.xlsx; do
  cp $f &quot;$DEST_DIR/&quot;    # WRONG — $f re-splits here
  cp &quot;$f&quot; &quot;$DEST_DIR/&quot;  # RIGHT — the quotes prevent the split
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even with a correct glob, an unquoted &lt;code&gt;$f&lt;/code&gt; in the &lt;code&gt;cp&lt;/code&gt; command re-splits on spaces. Bash has already expanded the variable to &lt;code&gt;Q3 final.xlsx&lt;/code&gt;, but when you use it unquoted, the shell processes word splitting again on that value and &lt;code&gt;cp&lt;/code&gt; receives two arguments: &lt;code&gt;Q3&lt;/code&gt; and &lt;code&gt;final.xlsx&lt;/code&gt;. The quotes around &lt;code&gt;&quot;$f&quot;&lt;/code&gt; tell bash to pass the entire value as a single argument. The rule is: glob over parse to build the list, quote on use to keep each item intact.&lt;/p&gt;
&lt;p&gt;I have seen this mistake repeated in scripts at three different companies. In each case the scripts had been running for months or years and the problem was invisible because the majority of files had no spaces. The ones that did — client names, quarterly reports, anything a non-technical person named — were being silently skipped or mishandled.&lt;/p&gt;
&lt;h3 id=&quot;ranges-and-counters-where-the-brace-trap-hides&quot;&gt;Ranges and counters: where the brace trap hides&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;n=5

# This does NOT produce a range — it produces the literal string {1..5}
for i in {1..$n}; do echo &quot;attempt $i&quot;; done

# This works — C-style, evaluates variables at runtime
for ((i = 1; i &amp;lt;= n; i++)); do echo &quot;attempt $i of $n&quot;; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Brace expansion happens before variable expansion in bash's order of operations. By the time &lt;code&gt;$n&lt;/code&gt; is replaced with its value, the brace expansion step has already passed and &lt;code&gt;{1..$n}&lt;/code&gt; is just a string. This is the kind of thing that fails silently in a test environment where the count is small and hardcoded, and causes weird output in production where it is a variable.&lt;/p&gt;
&lt;p&gt;The C-style loop is the correct form any time the bound is a variable. It evaluates at runtime and handles arithmetic naturally. If the count is genuinely fixed at write time, &lt;code&gt;{1..10}&lt;/code&gt; works fine. If it is ever going to be a variable, use &lt;code&gt;for (( ))&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;arrays-the-original-bug-wearing-a-different-hat&quot;&gt;Arrays: the original bug wearing a different hat&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;servers=(&quot;web-01&quot; &quot;db primary&quot; &quot;cache-02&quot;)

# WRONG — word-splits &quot;db primary&quot; into two iterations
for s in ${servers[@]}; do
  ping -c1 &quot;$s&quot;
done

# RIGHT — quotes keep each element intact
for s in &quot;${servers[@]}&quot;; do
  ping -c1 &quot;$s&quot;
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&quot;${servers[@]}&quot;&lt;/code&gt; with the quotes and &lt;code&gt;[@]&lt;/code&gt; is the form that preserves each element as a single item regardless of what is in it. Without the quotes, bash word-splits the array expansion and &lt;code&gt;db primary&lt;/code&gt; becomes two separate loop iterations — neither of which is a real hostname. This is exactly the same word-splitting mechanism as the &lt;code&gt;$(ls)&lt;/code&gt; problem, just manifesting in arrays instead of command output.&lt;/p&gt;
&lt;p&gt;Once you internalize that word-splitting happens wherever an unquoted variable or expansion appears, the rule becomes one rule instead of several: always quote expansions. The specific context changes; the mechanism does not.&lt;/p&gt;
&lt;h3 id=&quot;the-nullglob-case&quot;&gt;The nullglob case&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;shopt -s nullglob
for f in /data/exports/*.xlsx; do
  echo &quot;$f&quot;
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without &lt;code&gt;shopt -s nullglob&lt;/code&gt;, if no &lt;code&gt;.xlsx&lt;/code&gt; files exist, the glob &lt;code&gt;*.xlsx&lt;/code&gt; does not expand — it stays as the literal string &lt;code&gt;*.xlsx&lt;/code&gt;. The loop runs once with &lt;code&gt;f&lt;/code&gt; set to the literal string &lt;code&gt;/data/exports/*.xlsx&lt;/code&gt;. Your script then tries to process a file with that exact path, which does not exist, and produces an error or silently does nothing depending on what you do with it.&lt;/p&gt;
&lt;p&gt;Setting &lt;code&gt;nullglob&lt;/code&gt; tells bash to expand a non-matching glob to nothing (an empty list), so the loop simply does not run. This is almost always the right behavior when you are iterating files that might not exist.&lt;/p&gt;
&lt;h3 id=&quot;what-happened-to-the-q3-export&quot;&gt;What happened to the Q3 export&lt;/h3&gt;
&lt;p&gt;We recovered it from the CFO's local machine, where she had downloaded it before the backup was supposed to preserve it. The fix to the script took four minutes. The conversation about why the backup system had been failing silently for three weeks took longer. The monitoring that we added afterwards — a nightly check that the backup directory has at least as many files as the source directory — took another twenty minutes.&lt;/p&gt;
&lt;p&gt;The monitoring should have been there from the start. So should the glob. So should the &lt;code&gt;set -euo pipefail&lt;/code&gt; that would have made the copy failures loud instead of silent. These are things you add before something breaks, and the only reason to know you need them is to have seen, or caused, or read about what happens when they are missing.&lt;/p&gt;
&lt;p&gt;Full examples with the safe glob, counter, C-style loop, array form, and nullglob guard: &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/snippets/bash-for-loop-examples&quot;&gt;https://bashsnippets.xyz/snippets/bash-for-loop-examples&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For reading a file's lines one at a time, a for loop is the wrong tool — use &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/snippets/bash-read-file-line-by-line&quot;&gt;while IFS= read -r&lt;/a&gt; — and wrap any loop that touches real files in &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz/snippets/bash-error-handling&quot;&gt;set -euo pipefail&lt;/a&gt;. The rest is at &lt;a rel=&quot;nofollow&quot; href=&quot;https://bashsnippets.xyz&quot;&gt;https://bashsnippets.xyz&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20946/a-for-loop-skipped-every-file-with-a-space-and-called-the-backup-a-success</guid>
<pubDate>Thu, 18 Jun 2026 19:28:03 +0000</pubDate>
</item>
<item>
<title> Check out my CoderLegion profile</title>
<link>https://coderlegion.com/16963/check-out-my-coderlegion-profile</link>
<description>&lt;p&gt; Points: 101 |  Badges: 4 |  Followers: 0 |  Posts: 0&lt;/p&gt;
&lt;p&gt;View my full profile: &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/user/Emmanuel+Cortes&quot;&gt;https://coderlegion.com/user/Emmanuel Cortes&lt;/a&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/16963/check-out-my-coderlegion-profile</guid>
<pubDate>Thu, 07 May 2026 13:09:31 +0000</pubDate>
</item>
<item>
<title>A Soft Introduction to EasyShell</title>
<link>https://coderlegion.com/16702/a-soft-introduction-to-easyshell</link>
<description>&lt;blockquote&gt;&lt;p&gt;&quot;Can it be simpler?&quot; - A recurring thought I've had while working on build automation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/EasyShell&quot;&gt;EasyShell&lt;/a&gt; (command: &lt;code&gt;easy&lt;/code&gt;) is a minimal shell scripting language designed from the ground up for &lt;strong&gt;simplicity and cross-platform build/process automation&lt;/strong&gt;. If you’ve ever felt that PowerShell’s syntax is powerful but easy to forget, or that traditional shell scripts get messy across operating systems, EasyShell aims to strike a cleaner balance.&lt;/p&gt;
&lt;p&gt;It's implemented in C# and runs on .NET, giving you natural access to the entire .NET ecosystem while keeping the scripting experience lightweight and approachable.&lt;/p&gt;
&lt;h3 id=&quot;design-principles&quot;&gt;Design Principles&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;One command per line (shell-like flow)&lt;/li&gt;
&lt;li&gt;Very small set of core concepts&lt;/li&gt;
&lt;li&gt;Strongly-typed global variables&lt;/li&gt;
&lt;li&gt;Seamless .NET interop via reflection&lt;/li&gt;
&lt;li&gt;Expressions in parentheses for inline evaluation&lt;/li&gt;
&lt;li&gt;Easy to read, write, and remember&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/EasyShell/releases/tag/v0.1.0&quot;&gt;Version &lt;strong&gt;0.1.0&lt;/strong&gt;&lt;/a&gt; is now available — functional for everyday automation tasks.&lt;/p&gt;
&lt;h3 id=&quot;core-language-features&quot;&gt;Core Language Features&lt;/h3&gt;
&lt;h4 id=&quot;variables&quot;&gt;Variables&lt;/h4&gt;
&lt;p&gt;All variables are declared with an explicit type and live in global scope:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;INTVAR Count 42
BOOLVAR Debug TRUE
DOUBLEVAR Ratio 3.14
STRINGVAR Message &quot;Hello from EasyShell&quot;
HANDLEVAR Now System.DateTime.Now   # stores object instances
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Variable names are &lt;strong&gt;case-insensitive&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Reference with &lt;code&gt;$VarName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Re-assign with &lt;code&gt;$VarName = value&lt;/code&gt; (or expressions)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;expressions-the-power-feature-&quot;&gt;Expressions (The Power Feature)&lt;/h4&gt;
&lt;p&gt;Wrap sub-commands in parentheses to evaluate them inline:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;STRINGVAR Formatted (System.String.Format &quot;Count = {0}&quot; $Count)
BOOLVAR IsBig (&amp;gt; $Count 100)
STRINGVAR Today (format &quot;{0:yyyy-MM-dd}&quot; (GetDate))
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;calling-net&quot;&gt;Calling .NET&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Static members&lt;/strong&gt; (fully qualified):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;System.Console.WriteLine &quot;Hello World!&quot;
System.IO.File.WriteAllText &quot;output.txt&quot; $Message
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instance methods&lt;/strong&gt; via HANDLE:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;HANDLEVAR Now System.DateTime.Now
STRINGVAR Stamp (CALL $Now ToString)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;control-flow&quot;&gt;Control Flow&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;IF (== $Count 42)
    print &quot;The answer!&quot;
ELSEIF (&amp;gt; $Count 50)
    print &quot;Too high&quot;
ELSE
    print &quot;Something else&quot;
END

INTVAR I 0
WHILE (&amp;lt; $I 5)
    System.Console.WriteLine $I
    $I = (+ $I 1)   # arithmetic helpers coming soon
END
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;functions&quot;&gt;Functions&lt;/h4&gt;
&lt;p&gt;Simple named blocks (no parameters — use globals):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;FUNC BuildProject
    print &quot;Building...&quot;
    dotnet build
END

CALL BuildProject
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;external-commands&quot;&gt;External Commands&lt;/h4&gt;
&lt;p&gt;Any command that isn’t a keyword or .NET call falls back to executing an external process (with stdout capture):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;git status
dotnet publish ...
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;real-world-example&quot;&gt;Real-World Example&lt;/h3&gt;
&lt;p&gt;Check out the full build script used by the project itself:&lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/EasyShell/blob/main/BuildScripts/BuildEasyShell.easy&quot;&gt;BuildEasyShell.easy&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It demonstrates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Path handling and folder creation&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;dotnet publish&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Conditional logic for OS/architecture&lt;/li&gt;
&lt;li&gt;Creating a dated zip package&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This script is self-contained and shows how EasyShell can manage non-trivial automation tasks.&lt;/p&gt;
&lt;h3 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# REPL mode
easy

# Run a script
easy path/to/script.easy

# Help
easy --help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A dedicated &lt;strong&gt;VS Code syntax highlighting extension&lt;/strong&gt; is available: &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=Methodox.easyshell&quot;&gt;Methodox.easyshell&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;who-is-it-for-&quot;&gt;Who Is It For?&lt;/h3&gt;
&lt;p&gt;EasyShell is ideal for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build automation scripts&lt;/li&gt;
&lt;li&gt;Simple CI/CD glue&lt;/li&gt;
&lt;li&gt;Daily developer tooling&lt;/li&gt;
&lt;li&gt;Anyone who wants .NET power without heavy dependency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's intentionally minimal — no advanced Bash-style features, just the essentials done in a way that’s hard to forget.&lt;/p&gt;
&lt;p&gt;Give it a try and let me know what you think!&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/16702/a-soft-introduction-to-easyshell</guid>
<pubDate>Tue, 05 May 2026 02:31:45 +0000</pubDate>
</item>
<item>
<title>DevLog 20251214: Yet Another Shell Script – Keeping Build Automation Simple with EasyShell</title>
<link>https://coderlegion.com/16701/devlog-20251214-yet-another-shell-script-keeping-build-automation-simple-with-easyshell</link>
<description>&lt;p&gt;As we roll out more tools, both internal and public, there has been a growing need for easy-to-maintain shell scripting and build automation. Build automation is complex enough without all the syntax burden - my favorite is to use PowerShell, but often it feels like wrestling with the tool itself. PowerShell is powerful, sure, but its depth often means hunting down syntax I &lt;em&gt;know&lt;/em&gt; I've used a dozen times before. Bash has its quirks across platforms, and honestly I don't know good ways of using it outside MinGW, Git Bash, or WSL - none of which felt quite &quot;right&quot;.&lt;/p&gt;
&lt;p&gt;I wanted something lighter: a shell scripting tool that's &lt;strong&gt;easy to pick up, cross-platform, and hard to forget&lt;/strong&gt; once you start using it.&lt;/p&gt;
&lt;p&gt;So I made &lt;strong&gt;EasyShell&lt;/strong&gt; (or just &lt;code&gt;easy&lt;/code&gt;): a minimal shell scripting language implemented in C# that focuses on process automation with a very small set of concepts.&lt;/p&gt;
&lt;h3 id=&quot;motivation-and-design&quot;&gt;Motivation and Design&lt;/h3&gt;
&lt;p&gt;EasyShell is for &quot;lazy&quot; developers (like me) who just want to automate common tasks - calling .NET APIs, running external tools, handling simple logic - without a steep syntax curve or platform-specific headaches. It combines a shell-like one-command-per-line flow with strong typing and easy .NET interop. No advanced Bash-style wizardry; just straightforward scripting.&lt;/p&gt;
&lt;p&gt;It could also serve as a nice companion alongside other tools in the long-term Divooka ecosystem.&lt;/p&gt;
&lt;h3 id=&quot;quick-taste-of-the-syntax&quot;&gt;Quick Taste of the Syntax&lt;/h3&gt;
&lt;p&gt;Files typically use the &lt;code&gt;.easy&lt;/code&gt; extension, with syntax highlighting available via the &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=Methodox.easyshell&quot;&gt;VS Code extension&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here's a small sample from the repo:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-easy&quot;&gt;# Comments start with #
HANDLEVAR NOW System.DateTime.Now

# Instance method via CALL
STRINGVAR DATE (CALL $NOW ToString)

# Static .NET call with expression
STRINGVAR VALUE (System.String.Format &quot;Current time: {0}&quot; $DATE)

STRINGVAR PATH &quot;C:/temp/greeting.txt&quot;
System.IO.File.WriteAllText $PATH $VALUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Variables are declared with their type (&lt;code&gt;INTVAR&lt;/code&gt;, &lt;code&gt;STRINGVAR&lt;/code&gt;, &lt;code&gt;BOOLVAR&lt;/code&gt;, &lt;code&gt;DOUBLEVAR&lt;/code&gt;, &lt;code&gt;HANDLEVAR&lt;/code&gt;), names are case-insensitive, and everything lives in global scope for simplicity. Expressions are wrapped in parentheses for inline evaluation, like &lt;code&gt;(== $X 10)&lt;/code&gt; or sub-commands.&lt;/p&gt;
&lt;p&gt;Control flow sticks to the basics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IF ... ELSEIF ... ELSE ... END&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WHILE ... END&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Simple &lt;code&gt;FUNC ... END&lt;/code&gt; / &lt;code&gt;CALL&lt;/code&gt; (globals make &quot;returns&quot; easy via variables)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can call external executables naturally, and non-built-in commands fall through to process execution (stdout capture included).&lt;/p&gt;
&lt;h3 id=&quot;early-days&quot;&gt;Early Days&lt;/h3&gt;
&lt;p&gt;This is version 0.1.0 territory - functional for everyday build tasks, with room to grow (arithmetic helpers, more examples, etc.). You can run it in REPL mode (&lt;code&gt;easy&lt;/code&gt;) or point it at a script. Builds and packages are straightforward with the included scripts.&lt;/p&gt;
&lt;p&gt;If you've ever wished for something between a full PowerShell script and a quick batch file that &quot;just works&quot; across machines, maybe give EasyShell a try. The GitHub repo has the full language overview, examples, and build details: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MethodoxTech/EasyShell&quot;&gt;MethodoxTech/EasyShell&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/16701/devlog-20251214-yet-another-shell-script-keeping-build-automation-simple-with-easyshell</guid>
<pubDate>Tue, 05 May 2026 02:23:48 +0000</pubDate>
</item>
<item>
<title>Scripting done differently? (python, shell script, JS)</title>
<link>https://coderlegion.com/14748/scripting-done-differently-python-shell-script-js</link>
<description>&lt;p&gt;Most of the folks that have done scripting, already have this fixed idea in their heads, thats scripting means JS.&lt;/p&gt;
&lt;p&gt;That was the case for me as well till the day someone eventually asked when we would add pre and post requests in Voiden. Our instinct was to say &quot;ok, its coming soon&quot; and then build it quickly. But then we started thinking about all these hidden assumptions around scripting. So we took a bit more time and shipped something nice: scripting as a true runtime layer that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supports multiple languages, so you can use the one that fits your stack. &lt;/li&gt;
&lt;li&gt;Runs on real interpreters, not a limited sandbox.&lt;/li&gt;
&lt;li&gt;You can import packages and reuse your existing logic.&lt;/li&gt;
&lt;li&gt;Workflows are stateful, letting you chain requests and maintain context across them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Already works with JavaScript, Python and shell. Tomorrow, more languages will follow.&lt;/p&gt;
&lt;p&gt;The result is something we deeply care about: orchestration-level automation right inside your API client.&lt;/p&gt;
&lt;p&gt;check here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://voiden.md/download&quot;&gt;https://voiden.md/download&lt;/a&gt;&lt;br&gt;
repo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/VoidenHQ/voiden&quot;&gt;https://github.com/VoidenHQ/voiden&lt;/a&gt;&lt;/p&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/14748/scripting-done-differently-python-shell-script-js</guid>
<pubDate>Tue, 14 Apr 2026 07:21:38 +0000</pubDate>
</item>
<item>
<title>Let's check out the different methods to check PowerShell Version on Windows 11 with clear steps.</title>
<link>https://coderlegion.com/5929/lets-check-out-the-different-methods-to-check-powershell-version-windows-with-clear-steps</link>
<description>&lt;h3 id=&quot;how-to-check-powershell-version-on-windows-11-&quot;&gt;How to Check PowerShell Version on Windows 11?&lt;/h3&gt;
&lt;p&gt;Check PowerShell Version on Windows 11: Windows PowerShell is a command-line shell and scripting language built on top of the .NET Framework. Unlike the Command Prompt, PowerShell allows users to automate tasks, manage system configurations, and control Windows more effectively. The Automation in Windows PowerShell is extensive, supporting scripts, Loops, Functions, and Modules. Microsoft initially introduced it as Monad in its beta phase, which later became Windows PowerShell 1.0 on its 2006 official launch.&lt;/p&gt;
&lt;h3 id=&quot;why-should-you-check-your-powershell-version-on-windows-11-&quot;&gt;Why Should you Check your PowerShell Version on Windows 11?&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://winsides.com&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Different PowerShell Versions support different cmdlets, exhibit scripting capabilities, and provide security updates. For example, Windows 11 is pre-installed with PowerShell 5.1, which is compatible only with Windows. All of its cmdlets, modules, and scripts are designed for the Windows Ecosystem, whereas the latest Windows PowerShell Version 7 and above have cross-platform ability, and it allows users to run PowerShell 7 and above on other operating systems such as Linux and macOS.&lt;/p&gt;
&lt;p&gt;Additionally, PowerShell Version 5 receives only legacy security updates, whereas PowerShell 7 receives frequent security patches, performance improvements, and bug fixes. Each version has its own advantages and disadvantages, and in this article, let's discuss them in detail. Let's get started.&lt;/p&gt;
&lt;h3 id=&quot;different-methods-to-check-powershell-version-on-windows-11&quot;&gt;Different Methods to Check PowerShell Version on Windows 11&lt;/h3&gt;
&lt;p&gt;To determine which version of Windows PowerShell you are using, you can either use Windows PowerShell or you can use outside PowerShell methods. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Check PowerShell Version using PowerShell Commands&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get PowerShell Version outside Windows PowerShell&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easily Check Version on PowerShell 7&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;how-to-determine-powershell-version-using-powershell-commands-&quot;&gt;How to Determine PowerShell Version using PowerShell Commands?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In PowerShell, you can use various commands to get the PowerShell Version. The following are the steps. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Firstly, let's open Windows PowerShell using the Run Command. We can use the keyboard shortcut Win Key + R.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the Run, execute the following command to open Windows PowerShell. &lt;code&gt;powershell&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open PowerShell using the Run Command&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Run command will open PowerShell on Windows 11.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In PowerShell, we can use the following commands to determine PowerShell Version on Windows 11. &lt;/p&gt;
&lt;h3 id=&quot;1-using-psversiontable&quot;&gt;1 Using $PSVersionTable&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Execute the command &lt;code&gt;$PSVersionTable&lt;/code&gt; in Windows PowerShell. &lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=7107684184978588858&quot; alt=&quot;&quot;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;Note: If you only want to see the PowerShell version, then you can use the following command in the PowerShell $PSVersionTable.PSVersion. This command will precisely provide the determined PowerShell Version and related information. Kindly refer to the following image. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;With the above command, only the essential information, such as Major Version, Minor Version, Build, and Revision Number, will be displayed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;2-get-powershell-version-using-the-get-host-command&quot;&gt;2 Get PowerShell Version using the Get-Host Command&lt;/h3&gt;
&lt;p&gt;The Get-Host Command on Windows PowerShell provides information about the Console Host. The information is not related to the detailed PowerShell Version, however, we will get the PowerShell Version here. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To get the PowerShell Version using the Get-Host command, please execute it in PowerShell.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execute the &lt;code&gt;Get-Host&lt;/code&gt; Command in PowerShell&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=6715411517005703052&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The output will have the following information. Name, Version, InstanceID, UI, CurrentCulture, CurrentUICulture, Private Data, Debugger Enabled, IsRunspacePushed, Runspace. Here, the version determines PowerShell Version. &lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;3-determine-powershell-version-directly-using-host-version&quot;&gt;3 Determine PowerShell Version Directly using $host.version&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This command provides straightforward output similar to $PSVersionTable.PSVersion.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execute the following command in PowerShell. &lt;code&gt;$host.version&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The output will contain the following information. Major Build Number, Minor Build Number, Build Version, and Revision Version. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I am currently running Windows PowerShell 5.1.26100.6682. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;how-to-determine-powershell-version-outside-of-powershell-&quot;&gt;How to Determine PowerShell Version Outside of PowerShell?&lt;/h3&gt;
&lt;p&gt;Alright, the above methods explain how to check PowerShell Version using Windows PowerShell itself. Here, we can check how to check the Windows PowerShell Version outside of PowerShell, such as the Command Prompt and Registry Editor. &lt;/p&gt;
&lt;h3 id=&quot;get-powershell-version-using-command-prompt-&quot;&gt;Get PowerShell Version using Command Prompt:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go to the Run and open Command Prompt using the following command. &lt;code&gt;cmd&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the Command Prompt, kindly execute the command &lt;code&gt;powershell -Command &quot;$PSVersionTable.PSVersion&quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, the Command Prompt will display the PowerShell Version. In my case, I am on PowerShell 5.1.26100.6682. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;how-to-check-windows-powershell-version-on-windows-11-using-registry-editor-&quot;&gt;How to Check Windows PowerShell Version on Windows 11 using Registry Editor?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To open the registry editor on Windows 11, we will use the Run Command. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the Run, execute the command &lt;code&gt;regedit&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open Registry Editor using the Run Command&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The User Account Control will confirm and open Registry Editor. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to the following path in the Registry Editor. &lt;code&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once you navigate to the above path, you can find the file &quot;PowerShellVersion&quot;. You can view the version next to it. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can also open it to view the PowerShell Version. That's it. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;how-to-check-the-version-in-the-latest-powershell-7-&quot;&gt;How to check the Version in the latest PowerShell 7?&lt;/h3&gt;
&lt;p&gt;Windows PowerShell 7 is the modern, cross-platform form of PowerShell. Unlike the legacy PowerShell 5, which is built into Windows 11, we have to install PowerShell 7 separately. There are different methods available to install PowerShell 7 on Windows 11, from the Microsoft Store to Winget, MSI, and more. Here, we will explain how to check the PowerShell 7 version.&lt;/p&gt;
&lt;h3 id=&quot;install-powershell-7-using-winget-on-windows-11&quot;&gt;Install PowerShell 7 using WinGet on Windows 11&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open Command Prompt, and run the following command. &lt;code&gt;winget search powershell&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Winget will search the Microsoft Community Repository and Microsoft Store and provide the results. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From the results, you can locate the official stable build and install it using the following command. At the time of this article, the latest version of PowerShell is 7.5.3.0. It is the stable and official release. Kindly note down its ID. The ID is &lt;strong&gt;Microsoft.PowerShell&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locate official and stable PowerShell 7 and note down its ID&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next, we will install PowerShell 7 using the following command in the Command Prompt. &lt;code&gt;winget install --id Microsoft.PowerShell&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This command will install PowerShell 7 from the official GitHub repository. The process will take some time to complete. Kindly be patient. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the package is successfully installed, you can start using Windows PowerShell 7. You can explicitly access it via the Start menu or by using the command pwsh&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;Note: You can also download and install Windows PowerShell 7 using the MSI installer from the official GitHub Repository. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;determine-powershell-7-version-using-pwsh-command&quot;&gt;Determine PowerShell 7 version using pwsh command&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The following command will work only on the latest PowerShell 7 version. &lt;/li&gt;
&lt;li&gt;In PowerShell 7, execute the following command. pwsh --version&lt;/li&gt;
&lt;li&gt;PowerShell will display the version now. &lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;Note: In PowerShell 7, other legacy methods such as Get-Host, PSVersionTable will work flawlessly. Additonally, you can get PowerShell 7 version using the following command as well. pwsh -v&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;take-away&quot;&gt;Take Away&lt;/h3&gt;
&lt;p&gt;Whether you are using Legacy PowerShell 5 or the Modern PowerShell 7, the above article covers different methods and detailed steps to check the PowerShell Version on all variations. We hope you are satisfied with our article on How to Check PowerShell Version on Windows 11, and if you have any queries, kindly let us know in the Comments. &lt;/p&gt;
&lt;p&gt;For more interesting articles, stay tuned to Winsides.com. Happy Scripting! Peace out!&lt;/p&gt;
</description>
<category>PowerShell</category>
<guid isPermaLink="true">https://coderlegion.com/5929/lets-check-out-the-different-methods-to-check-powershell-version-windows-with-clear-steps</guid>
<pubDate>Fri, 03 Oct 2025 04:59:16 +0000</pubDate>
</item>
<item>
<title>DevLog 20250724: C# Scripting, Pure 2, Updated + Some Recap</title>
<link>https://coderlegion.com/4264/devlog-20250724-c-scripting-pure-2-updated-some-recap</link>
<description>&lt;h3 id=&quot;intro&quot;&gt;Intro&lt;/h3&gt;
&lt;p&gt;Pure has been our in-house C# scripting solution for a few years now.&lt;br&gt;
After experimenting with the well-known &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/CS-Script&quot;&gt;CS-Script&lt;/a&gt; and some other solutions I can no longer recall, I began experimenting with &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Roslyn_(compiler)&quot;&gt;Roslyn&lt;/a&gt; in .NET around 2022/2023, and it's been working well ever since.&lt;/p&gt;
&lt;h4 id=&quot;usage&quot;&gt;Usage&lt;/h4&gt;
&lt;p&gt;Below is a basic Pure script and its execution:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;using static System.Console;

var sum = File.ReadLines(&quot;Data.csv&quot;)
    .Skip(1)
    .Select(l =&amp;gt; l.Split(','))
    .Sum(parts =&amp;gt; double.Parse(parts[1]));
WriteLine(sum);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://publication.tor1.cdn.digitaloceanspaces.com/Websites/MethodoxWiki/Images/Utilities-CLI-Pure-Basic_Example.png&quot; alt=&quot;Result&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;recap&quot;&gt;Recap&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Version 0.0.3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1g0yfd5tu0wiq9natttt.png&quot; alt=&quot;v0.0.3&quot;&gt;&lt;/p&gt;
&lt;p&gt;The first version of Pure was just a simple wrapper around Roslyn. Later, we expanded it to support syntax like &lt;code&gt;Import(MathNet.Numerics)&lt;/code&gt;. It wasn't until we added native libraries like &lt;code&gt;CSV&lt;/code&gt;, &lt;code&gt;ODBC&lt;/code&gt;, and &lt;code&gt;CentralSnippets&lt;/code&gt; that things started to get interesting.&lt;/p&gt;
&lt;h3 id=&quot;motivation&quot;&gt;Motivation&lt;/h3&gt;
&lt;p&gt;Below is an old table I compiled when first drafting the design document for Pure (1), with some updates to mention newer solutions:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=292194321662759487&quot; alt=&quot;Table 1&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-s-special&quot;&gt;What's Special&lt;/h3&gt;
&lt;p&gt;Pure 2 is a lightweight C# scripting platform with modern C# 12 syntax and easy NuGet import support. Pure is based on Roslyn. In addition to the base .NET runtime, Pure offers additional libraries for handling common tasks, along with a handy scripting interface (REPL and Notebook) and a place where people can share their &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Pure-the-Language/CentralSnippets&quot;&gt;snippets&lt;/a&gt;.  &lt;/p&gt;
&lt;p&gt;Pure 2 is conceptually similar to &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Pure-the-Language/Pure&quot;&gt;Pure&lt;/a&gt; but architecturally very different - it directly builds upon Parcel NExT's Ama runtime (the same runtime used by Divooka). Pure is designed for very short snippets and getting everyday tasks done faster.&lt;/p&gt;
&lt;p&gt;Pure 2 uses C# 12 and is similar to &lt;a rel=&quot;nofollow&quot; href=&quot;https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statements&quot;&gt;top-level statements&lt;/a&gt; in C#, with the following features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can define types and functions anywhere in the middle of the script.&lt;/li&gt;
&lt;li&gt;Pure has its own package import behavior (default namespace usage and package lookup rules).&lt;/li&gt;
&lt;li&gt;Default global scope math functions, and default &lt;code&gt;using&lt;/code&gt; for &lt;code&gt;System.IO&lt;/code&gt; and &lt;code&gt;System.Linq&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Top-level function and variable definitions, alongside classes and other common C# constructs.&lt;/li&gt;
&lt;li&gt;Simplifies common tasks even further.&lt;/li&gt;
&lt;li&gt;Macros to support text-based parsing, file inclusion, and module imports.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;features&quot;&gt;Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Single-word standard package names. One-command NuGet package import and on-the-fly package invocation (through &lt;code&gt;Import&lt;/code&gt;).  &lt;/li&gt;
&lt;li&gt;Full compatibility with Divooka packages and Divooka graphs.&lt;/li&gt;
&lt;li&gt;Pure uses &lt;code&gt;PUREPATH&lt;/code&gt; to search for scripts when using &lt;code&gt;Include&lt;/code&gt; and when executing script files directly from the command line as the first argument.  &lt;/li&gt;
&lt;li&gt;Pure is very lightweight and will always be a thin layer on top of Roslyn/.NET runtime.  &lt;/li&gt;
&lt;li&gt;CLI REPL; GUI Notebook.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;basic-syntax&quot;&gt;Basic Syntax&lt;/h4&gt;
&lt;p&gt;Certain macros/syntax are provided to implement language-level scripting constructs:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8123867204314969076&quot; alt=&quot;Table 2&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Be aware that adopting a scripting system - any scripting system - comes with significant risks. It's wise to plan carefully and pick the best tools for the task at hand.&lt;/p&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Download page for Pure 2 on &lt;a rel=&quot;nofollow&quot; href=&quot;https://methodox.itch.io/pure-2&quot;&gt;Methodox Itch.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Official Page for Pure 2 on &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Utilities/CLI/Pure&quot;&gt;Methodox Wiki&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>C#</category>
<guid isPermaLink="true">https://coderlegion.com/4264/devlog-20250724-c-scripting-pure-2-updated-some-recap</guid>
<pubDate>Fri, 25 Jul 2025 01:39:40 +0000</pubDate>
</item>
<item>
<title>Automating With Python: A Developer’s Guide</title>
<link>https://coderlegion.com/287/automating-with-python-a-developers-guide</link>
<description>&lt;p&gt;In this guide, we'll explore Automation with Python. &lt;strong&gt;Automation&lt;/strong&gt; is key in today's fast-paced world, saving time and effort across various tasks. &lt;strong&gt;Python&lt;/strong&gt;, which is a top choice for &lt;strong&gt;Automation&lt;/strong&gt;. We'll cover its essential libraries along with examples for practical uses. By the end, you'll be ready to automate tasks efficiently using Python.&lt;/p&gt;
&lt;h2 id=&quot;understanding-automation&quot;&gt;Understanding Automation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is automation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automation&lt;/strong&gt; is the use of technology to perform different tasks with minimal human intervention. It involves the implementation of systems or processes that can &lt;strong&gt;operate automatically&lt;/strong&gt;, &lt;strong&gt;reducing the need for manual labor&lt;/strong&gt; and &lt;strong&gt;increasing efficiency&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why Automation is Important and Required:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Efficiency:&lt;/strong&gt; Streamlines processes, saving time and effort.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Accuracy:&lt;/strong&gt; Minimizes human error, ensuring precision.    &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cost Reduction:&lt;/strong&gt; Lowers labor costs, increases productivity.    &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability:&lt;/strong&gt; Adapts easily to changing demands.     &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Productivity:&lt;/strong&gt; Frees up time for strategic work.     &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Analysis:&lt;/strong&gt; Provides valuable insights for decision-making.   &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;introduction-of-python-in-automation&quot;&gt;Introduction of Python in Automation&lt;/h2&gt;
&lt;p&gt;Python is a powerful language for automation tasks due to its &lt;strong&gt;simplicity&lt;/strong&gt;, &lt;strong&gt;versatility&lt;/strong&gt;, and &lt;strong&gt;extensive libraries&lt;/strong&gt;. You can also visit &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/&quot;&gt;Python official site&lt;/a&gt; for more information. &lt;/p&gt;
&lt;h3 id=&quot;python-modules-for-automation&quot;&gt;Python Modules for Automation&lt;/h3&gt;
&lt;p&gt;Given below are the essential python Modules to automate various tasks along with their examples for better understanding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Selenium :&lt;/strong&gt; A powerful tool for automating web browsers, which allows you to interact with web elements programmatically. It's widely used for &lt;strong&gt;web scraping&lt;/strong&gt;, &lt;strong&gt;testing&lt;/strong&gt; and &lt;strong&gt;fill forms&lt;/strong&gt; in web applications.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

driver = webdriver.Chrome()
driver.get(&quot;https://www.google.com&quot;)
search_input = driver.find_element(By.NAME, &quot;q&quot;)

search_input.send_keys(&quot;Python automation&quot;)
search_input.send_keys(Keys.RETURN)

time.sleep(60)
driver.quit()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=6337230675310057768&quot; alt=&quot;selenium&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. PyAutoGUI :&lt;/strong&gt; This helps us to automate GUI interactions. It is useful for automating tasks involving graphical user interfaces, such as opening applications&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pyautogui
import time
try:
    pyautogui.press('win')
    pyautogui.write('notepad')
    pyautogui.press('enter')
    time.sleep(4)  # Wait for Notepad to open

    pyautogui.write('Hello')
    time.sleep(2)  # Let's type slowly for demonstration
    pyautogui.hotkey('ctrl', 's')
    time.sleep(4)  # Wait for Save As dialog to open
    pyautogui.write('Hello.txt')
    time.sleep(2)  
    pyautogui.press('enter')
    time.sleep(2)  

    print(&quot;Task completed successfully!&quot;)
except Exception as e:
    print(&quot;An error occurred:&quot;, e)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=14912766778711305660&quot; alt=&quot;pyautogui1&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=5646991223641803738&quot; alt=&quot;pyautogui2&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Requests :&lt;/strong&gt; This is a simple yet elegant &lt;strong&gt;HTTP library&lt;/strong&gt; for Python, which allows you to send HTTP requests easily. It's commonly used for web scraping, &lt;strong&gt;accessing APIs&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
try:
  response = requests.get(&quot;https://www.google.com&quot;)
  if response.status_code == 200:
      print(&quot;Request to Google successful!&quot;)
      print(&quot;HTML Content:&quot;)
      print(response.text)
  else:
      print(&quot;Failed to retrieve data from Google. Status code:&quot;, response.status_code)
except Exception as e:
  print(&quot;An error occurred:&quot;, e)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=13033853441911983193&quot; alt=&quot;request&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Beautiful Soup :&lt;/strong&gt; It provides us with different functions for &lt;strong&gt;parsing HTML and XML documents&lt;/strong&gt;. It allows you to extract data from web pages effortlessly, facilitating automation of data extraction processes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from bs4 import BeautifulSoup
import requests

response = requests.get(&quot;https://www.example.com&quot;)
#Parse the HTML content
soup = BeautifulSoup(response.content, &quot;html.parser&quot;)
for link in soup.find_all(&quot;a&quot;):
    print(link.get_text())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=1537387624587019714&quot; alt=&quot;beautiful2&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Pandas :&lt;/strong&gt; &lt;code&gt;pandas&lt;/code&gt; is a powerful &lt;strong&gt;data manipulation&lt;/strong&gt; library that help us to form data frames. It's commonly used for automating data processing tasks, such as cleaning, transforming, and analyzing datasets.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; import pandas as pd
 data = {
    'Name': ['Alice', 'Bob', 'Charlie', 'David'],
    'Age': [25, 30, 35, 40],
    'City': ['New York', 'Los Angeles', 'Chicago', 'Houston']
  }
df = pd.DataFrame(data)
print(&quot;DataFrame:&quot;)
print(df)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=5943730634121831101&quot; alt=&quot;dataframe1&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-started-with-automation&quot;&gt;Getting Started with Automation&lt;/h2&gt;
&lt;h3 id=&quot;how-to-automate-a-task-a-step-by-step-guide&quot;&gt;How to Automate a Task: A Step-by-Step Guide&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Identify Task&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Before moving to any planning the first step is to identify the repetitive and time-consuming tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Divide the Task into Smaller Steps&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Break down the task into smaller, manageable modules. Then start working on each module independently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Research Python Libraries&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Research for those Python libraries that can help us in automating each step of the task. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 4: Write the Code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Write Python code to automate each step of the task. Given below is a short example to get you started with scripting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import os
import shutil

file_types = set()
for filename in os.listdir('directory_path'):
    if os.path.isfile(filename):
        file_types.add(filename.split('.')[-1])
        
for file_type in file_types:
    os.makedirs(file_type, exist_ok=True)
    
for filename in os.listdir('directory_path'):
    if os.path.isfile(filename):
        file_type = filename.split('.')[-1]
        shutil.move(filename, os.path.join(file_type, filename))
    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 5: Test the Code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Test the code with sample data to ensure that it performs as expected. Verify that files are organized correctly into their respective folders.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=12988320224929339783&quot; alt=&quot;flow1&quot;&gt; &lt;br&gt;
&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=12229359629986255120&quot; alt=&quot;flow2&quot;&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 6: Update the Code&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Refine and update the code as needed based on test results and feedback. Ensure it handles edge cases and exceptions gracefully.&lt;/p&gt;
&lt;h2 id=&quot;introduction-to-automation-scripting-languages&quot;&gt;Introduction to Automation Scripting Languages&lt;/h2&gt;
&lt;p&gt;Scripting and automation have become &lt;strong&gt;indispensable&lt;/strong&gt; tools for individuals and organizations alike. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Understanding Automation Scripting Languages:&lt;/strong&gt;&lt;br&gt;
Automation scripting languages are programming languages that are specifically designed to automate repetitive tasks. These languages are having the predefined syntax and functionalities that are useful for automating tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Characteristics of Automation Scripting Languages:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Readable Syntax&lt;/strong&gt;: Clear and concise code for easier comprehension.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Abundance of Libraries&lt;/strong&gt;: Extensive pre-built functions streamline development.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interpretation or Compilation&lt;/strong&gt;: Code execution either directly (interpreted) or translated into machine language (compiled).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-Platform Compatibility&lt;/strong&gt;: Capable of running on various operating systems. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Popular Automation Scripting Languages:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Python&lt;/strong&gt;: Versatile, with rich libraries; used for automation, web dev, and data analysis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bash (Shell)&lt;/strong&gt;: Default on Unix; excels in system tasks and command-line ops.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PowerShell&lt;/strong&gt;: Microsoft's automation tool for Windows; robust system management.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;essential-python-libraries-for-automation&quot;&gt;Essential Python Libraries for Automation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;OS Module:&lt;/strong&gt; The &lt;code&gt;os&lt;/code&gt; module in Python provides a &lt;strong&gt;platform-independent&lt;/strong&gt; way of interacting with the &lt;strong&gt;operating system&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example :&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import os
cwd = os.getcwd()
print(&quot;Current directory:&quot;, cwd)

new_dir = os.path.join(cwd, 'new_directory')
if not os.path.exists(new_dir):
    os.makedirs(new_dir)
    print(&quot;New directory created:&quot;, new_dir)
else:
    print(&quot;Directory already exists:&quot;, new_dir)

files = os.listdir(cwd)
print(&quot;Files in current directory:&quot;, files)

old_file = os.path.join(cwd, 'old_file.txt')
new_file = os.path.join(cwd, 'new_file.txt')
os.rename(old_file, new_file)
print(&quot;File renamed from 'old_file.txt' to 'new_file.txt'&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=14821207025398722810&quot; alt=&quot;os_module&quot;&gt;&lt;br&gt;
&lt;/p&gt;&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;While renaming the files make sure to create the file whose name you want to change.&lt;/span&gt; &lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Shutil Module:&lt;/strong&gt; The &lt;code&gt;shutil&lt;/code&gt; module offers a high-level interface for &lt;strong&gt;file operations&lt;/strong&gt;, including file &lt;strong&gt;copying&lt;/strong&gt;, &lt;strong&gt;moving&lt;/strong&gt;, and &lt;strong&gt;deletion&lt;/strong&gt;. You can gain more knowledge from &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/shutil.html#module-shutil&quot;&gt;shutil module&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example :&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import shutil
source_file = 'C:\\Users\\dell\\Desktop\\temp\\source.txt'
destination_file = 'C:\\Users\\dell\\Desktop\\temp\\destination.txt'

try:
    shutil.copy(source_file, destination_file)
    print(&quot;File copied successfully!&quot;)
except FileNotFoundError:
    print(&quot;Source file not found.&quot;)
except PermissionError:
    print(&quot;Permission denied.&quot;)
except Exception as e:
    print(&quot;An error occurred:&quot;, e)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=16653162886812461037&quot; alt=&quot;shutil_module&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;Make sure to pass the correct path of the source and destination file in the same way as mentioned above.&lt;/span&gt; &lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Subprocess Module:&lt;/strong&gt; The &lt;code&gt;subprocess&lt;/code&gt; module provides a powerful way to &lt;strong&gt;spawn&lt;/strong&gt; new processes. It allows you to execute &lt;strong&gt;system commands&lt;/strong&gt;, &lt;strong&gt;run external programs&lt;/strong&gt;.For more details visit &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/subprocess.html&quot;&gt;subpress module&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example :&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import subprocess
try:
    #Run the command and capture output
    result = subprocess.run(['-l'], capture_output=True, text=True)
    # Print the command output
    print(&quot;Command output:&quot;, result.stdout)
except FileNotFoundError:
    print(&quot;Command not found.&quot;)
except Exception as e:
    print(&quot;An error occurred:&quot;, e)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=9371094365528278668&quot; alt=&quot;subprocess_module&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Time and Datetime Modules:&lt;/strong&gt; The &lt;code&gt;time&lt;/code&gt; and &lt;code&gt;datetime&lt;/code&gt; modules in Python provide functionalities for handling time-related tasks and scheduling automation jobs.For further information check &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.python.org/3/library/time.html#module-time&quot;&gt;Time module in python&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example :&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import time
delay = 5
start_time = time.time()
while time.time() &amp;lt; start_time + delay:
    pass
print(&quot;Automation job executed after {} seconds.&quot;.format(delay))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=1479053976879965214&quot; alt=&quot;time_date&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;When this code is executed than after the delay of 5 seconds the message is displayed&lt;/span&gt; &lt;/div&gt;
&lt;h2 id=&quot;web-scraping-and-automation&quot;&gt;Web Scraping and Automation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Web scraping&lt;/strong&gt; and &lt;strong&gt;Automation&lt;/strong&gt;, empower users to automate repetitive tasks and extract valuable data from websites automatically. The users are able to interact with the web pages and extract data automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automating Web Tasks&lt;/strong&gt;&lt;br&gt;
Automation scripts can interact with web pages, submit forms, extract data, and perform other tasks, enabling the automation of web-based workflows.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import requests
from bs4 import BeautifulSoup
#Define the URL of the webpage
url = &quot;https://www.example.com&quot;
try:
  response = requests.get(url)
  if response.status_code == 200:
      print(&quot;Webpage accessed successfully!&quot;)
      soup = BeautifulSoup(response.text, 'html.parser')
      links = soup.find_all('a')
      for link in links:
          print(link.text)
      # For example, submitting a login form
      payload = {'username': 'your_username', 'password': 'your_password'}
      login_response = requests.post('https://www.example.com/login', data=payload)
      if login_response.status_code == 200:
          print(&quot;Login successful!&quot;)
      else:
          print(&quot;Login failed.&quot;)
  else:
      print(&quot;Failed to access the webpage.&quot;)
except Exception as e:
  print(&quot;An error occurred:&quot;, e)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=13203608641888015506&quot; alt=&quot;web_scrap&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;automating-system-tasks&quot;&gt;Automating System Tasks&lt;/h2&gt;
&lt;p&gt;Automating system tasks involves streamlining repetitive processes on both &lt;strong&gt;Windows&lt;/strong&gt; and &lt;strong&gt;Linux&lt;/strong&gt; operating systems, enhancing &lt;strong&gt;productivity&lt;/strong&gt; and &lt;strong&gt;efficiency&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;task-automation-on-windows-and-linux&quot;&gt;Task Automation on Windows and Linux&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;On &lt;strong&gt;Windows&lt;/strong&gt;, PowerShell is a powerful automation tool that provides a wide range of cmdlets and scripts for managing system tasks. With &lt;strong&gt;PowerShell&lt;/strong&gt;, you can automate tasks such as &lt;strong&gt;file manipulation&lt;/strong&gt;, &lt;strong&gt;registry editing&lt;/strong&gt;, &lt;strong&gt;user management&lt;/strong&gt;, and &lt;strong&gt;system configuration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;On Linux, shell scripting with &lt;strong&gt;Bash&lt;/strong&gt; is commonly used for automating system tasks. Bash scripts can perform tasks such as &lt;strong&gt;directory traversal&lt;/strong&gt;, &lt;strong&gt;process management&lt;/strong&gt;, and &lt;strong&gt;system monitoring&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example :&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$sourceDir = &quot;C:\Users\dell\Desktop\File1.txt&quot;
$destDir = &quot;C:\Users\dell\Desktop\File2.txt&quot;

$timestamp = Get-Date -Format &quot;yyyyMMdd_HHmmss&quot;
$backupFile = &quot;backup_$timestamp.tar.gz&quot;

Compress-Archive -Path $sourceDir -DestinationPath &quot;$destDir\$backupFile&quot;
Write-Host &quot;Backup created: $destDir\$backupFile&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=1433631920887171787&quot; alt=&quot;powershell&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;data-processing-and-automation&quot;&gt;Data Processing and Automation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Introduction to Data Processing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Data processing&lt;/strong&gt; is the method of transforming raw data into valuable information or insights. It involves several stages such as &lt;strong&gt;data collection&lt;/strong&gt;, &lt;strong&gt;cleaning&lt;/strong&gt;, &lt;strong&gt;transformation&lt;/strong&gt;, &lt;strong&gt;analysis&lt;/strong&gt;, and &lt;strong&gt;visualization&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using Pandas for Data Automation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When the Python library &lt;code&gt;Pandas&lt;/code&gt; is used for data automation then it allows efficient &lt;strong&gt;processing&lt;/strong&gt;, &lt;strong&gt;manipulation&lt;/strong&gt;, and &lt;strong&gt;analysis&lt;/strong&gt; of data. &lt;code&gt;Pandas&lt;/code&gt; is a powerful library that provides data structures such as &lt;code&gt;DataFrame&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example :&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pandas as pd
data = pd.read_csv('data.csv')
data.head(7)
df = pd.DataFrame(data)
df.head()
missing_values = df.isna().sum()
print(missing_values)
df_filled = df.fillna(0)
df_filled.head(7)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=15419402808891249462&quot; alt=&quot;panda1&quot;&gt; &lt;/p&gt;
&lt;h2 id=&quot;testing-and-automation&quot;&gt;Testing and Automation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Testing&lt;/strong&gt; and &lt;strong&gt;automation&lt;/strong&gt; are crucial aspects of software development, ensuring &lt;strong&gt;software reliability&lt;/strong&gt; and &lt;strong&gt;quality&lt;/strong&gt;. It involves systematically evaluating software to find defects and built test cases for testing purpose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overview of Testing Automation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testing automation&lt;/strong&gt; involves the use of software tools and frameworks to automate the process of testing software applications. One popular testing automation framework is &lt;strong&gt;&lt;code&gt;pytest&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;using-pytest-for-testing-automation&quot;&gt;Using pytest for Testing Automation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Pytest&lt;/strong&gt; is a popular testing framework in Python that is used for testing automation. It offers powerful features for writing and executing test cases efficiently. With &lt;code&gt;pytest&lt;/code&gt;, developers can easily create &lt;strong&gt;test functions&lt;/strong&gt;, &lt;strong&gt;organize test suites&lt;/strong&gt;, and &lt;strong&gt;assert expected outcomes&lt;/strong&gt;. Its suitable for both &lt;strong&gt;small&lt;/strong&gt; and &lt;strong&gt;large-scale projects&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example :&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Calculator:
    def add(self, a, b):
        return a + b
   def subtract(self, a, b):
         return a - b
calculator = Calculator()
def test_addition():
    assert calculator.add(2, 3)==5
    assert calculator.add(5, -1)==4
def test_subtraction():
    assert calculator.subtract(5, 3)==2
    assert calculator.subtract(10, 7)==3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=7916416413820548395&quot; alt=&quot;py_test&quot;&gt;&lt;/p&gt;
&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt;Always save your `pytest` file with the following naming convention that is `test_filename`.&lt;/span&gt; &lt;/div&gt;
&lt;h2 id=&quot;real-world-automation-examples&quot;&gt;Real-World Automation Examples&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Backup Automation&lt;/strong&gt;: Schedule regular backups of critical data to prevent loss in case of hardware failure or data corruption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Maintenance&lt;/strong&gt;: Automate routine server maintenance tasks such as software updates, disk cleanup, and system reboots.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Email Automation&lt;/strong&gt;: Implement automated email responses, email filtering, and email forwarding to streamline communication processes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Migration&lt;/strong&gt;: Automate the migration of data between databases or cloud storage solutions to ensure accuracy and efficiency.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Report Generation&lt;/strong&gt;: Automatically generate and distribute reports on a scheduled basis, reducing manual effort and ensuring timely delivery.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customer Support&lt;/strong&gt;: Implement chatbots or automated ticketing systems to handle common customer inquiries and support requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In conclusion it is better to say that, &lt;strong&gt;Automation&lt;/strong&gt; with &lt;strong&gt;Python&lt;/strong&gt; empowers individuals and organizations to &lt;strong&gt;streamline workflows&lt;/strong&gt;, increase &lt;strong&gt;productivity&lt;/strong&gt;, and &lt;strong&gt;drive innovation&lt;/strong&gt;. Python's simplicity, versatility, and rich ecosystem of libraries, automation can be used to automate tasks across diverse domains, from &lt;strong&gt;file operations&lt;/strong&gt; and &lt;strong&gt;web scraping&lt;/strong&gt; to &lt;strong&gt;system administration&lt;/strong&gt; and &lt;strong&gt;data processing&lt;/strong&gt;. Do gather some more information by doing some experiment with different libraries and tools, and apply best practices that you learned in this guide to give &lt;strong&gt;efficient&lt;/strong&gt; and &lt;strong&gt;reliable&lt;/strong&gt; automation solutions.&lt;/p&gt;
</description>
<category>Automation</category>
<guid isPermaLink="true">https://coderlegion.com/287/automating-with-python-a-developers-guide</guid>
<pubDate>Tue, 14 May 2024 22:30:57 +0000</pubDate>
</item>
<item>
<title>Leveraging Python for System Administration</title>
<link>https://coderlegion.com/262/leveraging-python-for-system-administration</link>
<description>&lt;p&gt;In the world of computer systems, keeping things running smoothly is a top priority, and that's where &lt;strong&gt;system administrators&lt;/strong&gt; come in. &lt;strong&gt;Python&lt;/strong&gt;, with its user-friendly interface and powerful capabilities, is like a trusty option. This article dives into how Python empowers system administrators to &lt;strong&gt;automate&lt;/strong&gt; tasks and &lt;strong&gt;optimize&lt;/strong&gt; resources, offering practical insights and real-world examples to support professionals at all levels in mastering this essential tool.&lt;/p&gt;
&lt;h2 id=&quot;understanding-system-administration&quot;&gt;Understanding System Administration&lt;/h2&gt;
&lt;p&gt;In system administration, you're responsible for managing, maintaining, and operating computer systems, networks, and associated infrastructure within your organization.  System administrators, commonly known as sysadmins, are integral in guaranteeing the efficient operation and security of IT systems to uphold business functions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User Management:&lt;/strong&gt; This involves managing user accounts, permissions, access rights, password resets, account lockouts, and user group management for effective access control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File System Management:&lt;/strong&gt; You'll manage file systems, including tasks like creating, modifying, and deleting files and directories, setting permissions, managing disk space, and monitoring file system health to prevent data loss or corruption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Software Installation:&lt;/strong&gt; Installing and configuring software applications involves selecting, installing, and configuring software packages on servers and workstations to ensure optimal performance and security.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configuration Management:&lt;/strong&gt; You'll manage the configuration of system components like servers, network devices, and software applications, ensuring consistency, currency, and compliance with organizational standards and best practices.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;challenges-in-traditional-system-administration&quot;&gt;Challenges in Traditional System Administration&lt;/h2&gt;
&lt;p&gt;Traditional system administration tasks are known for being manual and repetitive, often requiring sysadmins to perform the same actions across multiple systems. While essential for IT infrastructure functionality and security, these tasks can be time-consuming, error-prone, and resource-intensive.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Volume of Work:&lt;/strong&gt; Sysadmins handle daily routine tasks such as managing user accounts, software updates, and configuration changes, which can become overwhelming in large-scale environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Human Error:&lt;/strong&gt; Manual tasks can lead to mistakes like misconfigurations, typos, and oversights, resulting in system downtime, data loss, security vulnerabilities, and compliance violations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lack of Consistency:&lt;/strong&gt; Without predefined processes and automation tools, inconsistencies may arise due to ad-hoc methods and shortcuts, leading to compliance issues.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability and Agility:&lt;/strong&gt; Manual processes hinder scalability and adaptability to changing business needs, becoming increasingly burdensome and inefficient as organizations grow and IT environments become more complex.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the world of IT, managing large-scale systems is like navigating a maze filled with challenges and complexities. As organizations grow, so does the size and complexity of their systems, presenting sysadmins with a host of hurdles to overcome.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automation Complexity:&lt;/strong&gt; Automating large-scale environments is complex and time-consuming, involving the development, testing, and maintenance of automated workflows across diverse systems. This requires careful planning and coordination.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visibility and Monitoring:&lt;/strong&gt; Maintaining visibility into large-scale system health, performance, and security is challenging. Insufficient monitoring tools may create blind spots, hindering prompt issue identification and resolution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security Risks:&lt;/strong&gt; Large-scale systems are prime targets for cyber threats. Ensuring robust security measures like access controls, encryption, and threat detection is crucial but challenging in dynamic environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Change Management:&lt;/strong&gt; Implementing changes, updates, and upgrades in large-scale systems requires meticulous planning and coordination.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compliance and Governance:&lt;/strong&gt; Comprehensive governance strategies are crucial for documentation, policy enforcement, and compliance across diverse environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;essential-python-libraries-for-system-administration&quot;&gt;Essential Python Libraries for System Administration&lt;/h2&gt;
&lt;p&gt;Python is a valuable asset for system administrators, offering a range of libraries to streamline tasks and boost productivity. These libraries empower sysadmins to interact with operating systems, automate tasks, and manage infrastructure effectively.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;os Module&lt;/strong&gt;: The &lt;code&gt;os&lt;/code&gt; module in Python offers a plethora of functions for interacting with the operating system. It enables sysadmins to navigate file systems, manipulate files and directories, and manage processes efficiently.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;subprocess Module&lt;/strong&gt;: The &lt;code&gt;subprocess&lt;/code&gt; module in Python allows scripts to spawn new processes, interact with system programs, and execute shell commands. It provides a higher-level interface than the &lt;code&gt;os.system()&lt;/code&gt; function.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;sys Module&lt;/strong&gt;: The &lt;code&gt;sys&lt;/code&gt; module in Python provides access to system-specific parameters and functions, allowing interaction with the Python interpreter and retrieval of information about the runtime environment, command-line arguments, and standard input/output streams.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;shutil Module&lt;/strong&gt;: The &lt;code&gt;shutil&lt;/code&gt; module in Python provides a high-level interface for file and directory operations, including copying, moving, renaming, deleting, archiving, and compressing files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;paramiko Module&lt;/strong&gt;: The &lt;code&gt;paramiko&lt;/code&gt; module in Python is a powerful library for SSH communication and remote server management. It allows sysadmins to securely connect to remote servers, execute commands, transfer files, and manage SSH keys programmatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;psutil Module&lt;/strong&gt;: The &lt;code&gt;psutil&lt;/code&gt; module in Python offers a cross-platform interface for accessing information on running processes, system utilization, and resources. It enables sysadmins to monitor system performance, manage processes, and collect detailed data on CPU, memory, disk, and network usage.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;div-blue&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Note:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt; Although the aforementioned modules usually come pre-installed with Python, you may install a module using the command: `pip install module_name` if it's not present in your Python environment. Import a module using the command: `import module_name`.&lt;/span&gt; &lt;/div&gt;
&lt;h2 id=&quot;examples-of-python-scripts-for-system-administration&quot;&gt;Examples of Python Scripts for System Administration&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;User Management&lt;/strong&gt; We can create, modify, and delete user accounts using Python by calling system commands (&lt;code&gt;useradd&lt;/code&gt;, &lt;code&gt;passwd&lt;/code&gt;, &lt;code&gt;userdel&lt;/code&gt;) via the &lt;code&gt;subprocess&lt;/code&gt; module.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creating User Account:&lt;/strong&gt; The process of adding a new user to the system with specified attributes such as username, password, and home directory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import subprocess
  
  def create_user(username, password):
      try:
          subprocess.run(['powershell', '-Command', f'New-LocalUser -Name {username} -Password (ConvertTo-SecureString &quot;{password}&quot; -AsPlainText -Force)'])
          print(f&quot;User '{username}' created successfully.&quot;)
      except subprocess.CalledProcessError as e:
          print(f&quot;Error creating user '{username}': {e}&quot;)

  create_user(&quot;Sia&quot;, &quot;si@123&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  The equivalent Linux command can be obtained by replacing the main_com line by:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  subprocess.run(['useradd', '-m', username])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  The script uses the &lt;code&gt;subprocess&lt;/code&gt; module to execute a PowerShell command, creating a new local user account on a Windows system. The &lt;code&gt;create_user&lt;/code&gt; function constructs a PowerShell command with the provided username and password, utilizing the &lt;code&gt;New-LocalUser&lt;/code&gt; cmdlet. Upon successful creation, a confirmation message is displayed; otherwise, an error message is shown.&lt;/p&gt;
&lt;p&gt;  Output:&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=11788929685447182849&quot; alt=&quot;create_user&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modifying User Account:&lt;/strong&gt; Making changes to an existing user account, such as updating user information or modifying permissions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import subprocess

  def modify_user(username, new_password):
      try:
          subprocess.run(['powershell', '-Command', f'Set-LocalUser -Name {username} -Password (ConvertTo-SecureString &quot;{new_password}&quot; -AsPlainText -Force)'])
          print(f&quot;Password for user '{username}' modified successfully.&quot;)
      except subprocess.CalledProcessError as e:
          print(f&quot;Error modifying password for user '{username}': {e}&quot;)

  modify_user(&quot;Sia&quot;, &quot;si@456&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  The equivalent Linux command can be obtained by replacing the main_com line by:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  subprocess.run(['passwd', username], input=new_password.encode())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  The script employs the &lt;code&gt;subprocess&lt;/code&gt; module to execute a PowerShell command, altering the password of an existing local user account. The &lt;code&gt;modify_user&lt;/code&gt; function accepts parameters for the username and the new password, constructing a PowerShell command using string formatting or &lt;code&gt;passwd&lt;/code&gt; command. If the operation succeeds, a confirmation message is shown.&lt;/p&gt;
&lt;p&gt;  Output:&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=2109637662964722996&quot; alt=&quot;modify_user&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deleting User Account:&lt;/strong&gt; Removing an existing user account from the system, including all associated files and directories.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import subprocess

  def delete_user(username):
      try:
          subprocess.run(['powershell', '-Command', f'Remove-LocalUser -Name {username} -Force'])
          print(f&quot;User '{username}' deleted successfully.&quot;)
      except subprocess.CalledProcessError as e:
          print(f&quot;Error deleting user '{username}': {e}&quot;)

  delete_user(&quot;Sia&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  The equivalent Linux command can be obtained by replacing the main_com line by:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  subprocess.run(['userdel', '-r', username])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  The script uses the &lt;code&gt;subprocess&lt;/code&gt; module to execute a PowerShell command, deleting an existing local user account. The &lt;code&gt;delete_user&lt;/code&gt; function accepts a &lt;code&gt;username&lt;/code&gt; parameter indicating the user account to be deleted. It constructs a PowerShell command using string formatting, utilizing the &lt;code&gt;Remove-LocalUser&lt;/code&gt;  or &lt;code&gt;userdel&lt;/code&gt; cmdlet. After deletion, a success message is displayed.&lt;/p&gt;
&lt;p&gt;  Output:&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=15163740878505794492&quot; alt=&quot;delete_user&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;div-red&quot;&gt; &lt;span class=&quot;alert-header&quot;&gt;Caution:&lt;/span&gt; &lt;span class=&quot;alert-body&quot;&gt; Ensure that the script is executed with appropriate privileges to perform these actions.&lt;/span&gt; &lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Setting User Permissions and Managing Access Control&lt;/strong&gt; To set user permissions and manage access control with a script, you can utilize the &lt;code&gt;subprocess&lt;/code&gt; module in Python to execute system commands for manipulating file permissions and access control lists (ACLs).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import subprocess

def set_permissions(file_path, user, permission):
    try:
        subprocess.run(['icacls', file_path, '/grant', f'{user}:{permission}'])
        print(f&quot;Permissions set successfully for user '{user}' on '{file_path}'&quot;)
    except subprocess.CalledProcessError as e:
        print(f&quot;Error setting permissions: {e}&quot;)

file_path = &quot;myfile.txt&quot;
user = &quot;Sia&quot;
permission = &quot;F&quot;  # Full control
set_permissions(file_path, user, permission)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The equivalent Linux command can be obtained by replacing the main_com line by:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;subprocess.run(['chmod', permission, file_path])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script uses the &lt;code&gt;icacls&lt;/code&gt; command to modify file permissions in Windows. The &lt;code&gt;/grant&lt;/code&gt; option specifies user and permissions, while the &lt;code&gt;permission&lt;/code&gt; parameter allows customization using flags like F, M, RX, R, W, D.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File System Management&lt;/strong&gt; To automate file and directory operations such as creation, deletion, and modification in Python, you can use the &lt;code&gt;os&lt;/code&gt; module for basic file and directory operations, and the &lt;code&gt;shutil&lt;/code&gt; module for more advanced operations like copying and moving files.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Creation, Modification and Deletion&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import os

  def create_file(file_path):
      try:
          with open(file_path, 'w') as f:
              pass  # Creating an empty file
          print(f&quot;File '{file_path}' created successfully.&quot;)
      except Exception as e:
         print(f&quot;Error creating file '{file_path}': {e}&quot;)

  def delete_file(file_path):
      try:
          os.remove(file_path)
          print(f&quot;File '{file_path}' deleted successfully.&quot;)
      except Exception as e:
          print(f&quot;Error deleting file '{file_path}': {e}&quot;)

  def modify_file(file_path, content):
      try:
          with open(file_path, 'w') as f:
              f.write(content)
          print(f&quot;File '{file_path}' modified successfully.&quot;)
      except Exception as e:
          print(f&quot;Error modifying file '{file_path}': {e}&quot;)

  file_path = &quot;sample.txt&quot;
  create_file(file_path)
  modify_file(file_path, &quot;I hope you are enjoying so far!&quot;)
  delete_file(file_path)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;code&gt;create_file&lt;/code&gt; creates an empty file at the specified path, &lt;code&gt;modify_file&lt;/code&gt; modifies the content of an existing file with the provided content and &lt;code&gt;delete_file&lt;/code&gt; deletes the file at the specified path.&lt;/p&gt;
&lt;p&gt;  Output:&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=2720371379524164168&quot; alt=&quot;sampletxt&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Directory Creation, Modification and Deletion&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import os

  def create_directory(directory_path):
      try:
          os.makedirs(directory_path, exist_ok=True)
          print(f&quot;Directory '{directory_path}' created successfully.&quot;)
      except Exception as e:
          print(f&quot;Error creating directory '{directory_path}': {e}&quot;)

  def delete_directory(directory_path):
      try:
          os.rmdir(directory_path)
          print(f&quot;Directory '{directory_path}' deleted successfully.&quot;)
      except Exception as e:
          print(f&quot;Error deleting directory '{directory_path}': {e}&quot;)

  def rename_directory(directory_path, new_name):
      try:
          os.rename(directory_path, new_name)
          print(f&quot;Directory '{directory_path}' renamed to '{new_name}' successfully.&quot;)
      except Exception as e:
          print(f&quot;Error renaming directory '{directory_path}': {e}&quot;)

  directory_path = &quot;sampledir&quot;
  new_name = &quot;samp_dir&quot;
  create_directory(directory_path)
  rename_directory(directory_path, new_name)
  delete_directory(new_name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;code&gt;create_directory&lt;/code&gt; creates a new directory at the specified path, &lt;code&gt;rename_directory&lt;/code&gt; renames an existing directory to the specified new name and &lt;code&gt;delete_directory&lt;/code&gt; deletes the directory at the specified path.&lt;/p&gt;
&lt;p&gt;  Output:&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=13058014505003097498&quot; alt=&quot;sampledir&quot;&gt;&lt;/p&gt;
&lt;p&gt;  After renaming:&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=544211272032465415&quot; alt=&quot;samp_dir&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Software Installation and Configuration&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automating Software Installation and Updates&lt;/strong&gt; Automating software installation and updates using package managers like &lt;code&gt;pip&lt;/code&gt; or system-specific tools can be achieved through Python scripts.&lt;/p&gt;
&lt;p&gt;  For example, to install and update the &lt;code&gt;numpy&lt;/code&gt; package:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import subprocess
   
  def install_package(package_name):
      try:
          subprocess.run(['pip', 'install', package_name])
          print(f&quot;Package '{package_name}' installed successfully.&quot;)
      except subprocess.CalledProcessError as e:
          print(f&quot;Error installing package '{package_name}': {e}&quot;)
   
  def update_package(package_name):
      try:
          subprocess.run(['pip', 'install', '--upgrade', package_name])
          print(f&quot;Package '{package_name}' updated successfully.&quot;)
      except subprocess.CalledProcessError as e:
          print(f&quot;Error updating package '{package_name}': {e}&quot;)

  package_name = &quot;numpy&quot;
  install_package(package_name)
  update_package(package_name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  The &lt;code&gt;install_package&lt;/code&gt; function installs a Python package using the &lt;code&gt;pip install&lt;/code&gt; command. The &lt;code&gt;update_package&lt;/code&gt; function updates a Python package to the latest version using the &lt;code&gt;pip install --upgrade&lt;/code&gt; command.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuring Software Settings and Parameters&lt;/strong&gt; You can use the &lt;code&gt;sys&lt;/code&gt; module in Python to configure software settings programmatically on Windows, particularly for command-line arguments handling.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  import sys
  import subprocess

  def configure_with_tool(tool_path, arguments):
      try:
          subprocess.run([tool_path] + arguments, check=True)
          print(&quot;Configuration successful.&quot;)
      except subprocess.CalledProcessError as e:
          print(f&quot;Error configuring with tool '{tool_path}': {e}&quot;)

  if __name__ == &quot;__main__&quot;:
      if len(sys.argv) &amp;lt; 3:
          print(&quot;Usage: python script.py &amp;lt;tool_path&amp;gt; &amp;lt;arguments&amp;gt;&quot;)
          sys.exit(1)

      tool_path = sys.argv[1]
      arguments = sys.argv[2:]
      configure_with_tool(tool_path, arguments)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When you run the script from the command line, you'll provide the tool path as the first argument as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python Articles/software_settings.py &quot;Articles//SimpleCalculator.exe&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;System Monitoring and Reporting&lt;/strong&gt; You can use Python to gather system metrics such as CPU usage, memory usage, and disk space using various libraries and system commands.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import psutil

def get_cpu_usage():
    cpu_usage = psutil.cpu_percent(interval=1)
    return cpu_usage

def get_memory_usage():
    memory = psutil.virtual_memory()
    total_memory = memory.total
    available_memory = memory.available
    used_memory = memory.used
    memory_usage_percent = memory.percent
    return {
        &quot;total_memory&quot;: total_memory,
        &quot;available_memory&quot;: available_memory,
        &quot;used_memory&quot;: used_memory,
        &quot;memory_usage_percent&quot;: memory_usage_percent
    }

def get_disk_usage():
    partitions = psutil.disk_partitions()
    disk_usage = {}
    for partition in partitions:
        partition_usage = psutil.disk_usage(partition.mountpoint)
        disk_usage[partition.mountpoint] = {
            &quot;total&quot;: partition_usage.total,
            &quot;used&quot;: partition_usage.used,
            &quot;free&quot;: partition_usage.free,
            &quot;percent&quot;: partition_usage.percent
        }
    return disk_usage

cpu_usage = get_cpu_usage()
memory_usage = get_memory_usage()
disk_usage = get_disk_usage()
print(&quot;CPU Usage:&quot;, cpu_usage, &quot;%&quot;)
print(&quot;Memory Usage:&quot;, memory_usage)
print(&quot;Disk Usage:&quot;, disk_usage)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;get_cpu_usage&lt;/code&gt; function retrieves CPU usage as a percentage, while &lt;code&gt;get_memory_usage&lt;/code&gt; gathers memory statistics, and &lt;code&gt;get_disk_usage&lt;/code&gt; collects disk stats. These functions utilize &lt;code&gt;psutil&lt;/code&gt; methods for data retrieval.&lt;/p&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://logiclair.org/?qa=blob&amp;amp;qa_blobid=15919331785992180360&quot; alt=&quot;sys_metrics&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;System Administration faces challenges with manual tasks and the need for efficiency. Python offers a solution with its libraries like &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;subprocess&lt;/code&gt;, and &lt;code&gt;shutil&lt;/code&gt;, making it ideal for &lt;strong&gt;automation&lt;/strong&gt;. Python scripts can handle &lt;strong&gt;user management&lt;/strong&gt;, &lt;strong&gt;permissions&lt;/strong&gt;, &lt;strong&gt;access control&lt;/strong&gt;, &lt;strong&gt;file system&lt;/strong&gt; operations, software &lt;strong&gt;installation&lt;/strong&gt;, and system &lt;strong&gt;monitoring&lt;/strong&gt;. This empowers administrators to enhance productivity and proactively manage computer systems effectively.&lt;/p&gt;
</description>
<category>Automation</category>
<guid isPermaLink="true">https://coderlegion.com/262/leveraging-python-for-system-administration</guid>
<pubDate>Wed, 01 May 2024 21:40:14 +0000</pubDate>
</item>
</channel>
</rss>