<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Monzool&#039;s Personal Publishing</title>
	<atom:link href="https://monzool.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://monzool.net/blog/</link>
	<description>a/ Jan Skriver Sørensen</description>
	<lastBuildDate>Mon, 13 Apr 2026 08:18:19 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Error banner in shell</title>
		<link>https://monzool.net/blog/2026/04/10/error-banner-in-shell/</link>
					<comments>https://monzool.net/blog/2026/04/10/error-banner-in-shell/#respond</comments>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Fri, 10 Apr 2026 14:03:32 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Shell Scripting]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=934</guid>

					<description><![CDATA[<p>WORKING FAST AND furious can be great when you are in the zone, but sometimes I work too fast for my own good. Way too often than I&#8217;d care to admit, I will quickly create a new branch: Then pop the stash and/or do the fast and furious writing thing. [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2026/04/10/error-banner-in-shell/">Error banner in shell</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>WORKING FAST AND</strong> furious can be great when you are in the <em>zone</em>, but sometimes I work too fast for my own good.</p>



<p>Way too often than I&#8217;d care to admit, I will quickly create a new branch:</p>



<pre class="wp-block-code"><code>❱ git switch feature/&#x1f525;
fatal: invalid reference: feature/&#x1f525;
</code></pre>



<p>Then pop the stash and/or do the fast and furious writing thing. Much later I would discover that I had forgotten the <code>-c</code> option to actually create a new branch, realizing I just messed up another unrelated branch <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f926.png" alt="🤦" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Obviously I need something to bring the error exit more to my attention, so I cooked up this little attention functionality</p>



<p>Now, when git fails, this banner pops up in the shell </p>



<figure class="wp-block-image size-full"><a href="https://monzool.net/blog/wp-content/uploads/2026/04/guru-1.png"><img fetchpriority="high" decoding="async" width="530" height="143" src="https://monzool.net/blog/wp-content/uploads/2026/04/guru-1.png" alt="" class="wp-image-935" srcset="https://monzool.net/blog/wp-content/uploads/2026/04/guru-1.png 530w, https://monzool.net/blog/wp-content/uploads/2026/04/guru-1-300x81.png 300w" sizes="(max-width: 530px) 100vw, 530px" /></a></figure>



<p></p>



<p>Perfect <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f44c.png" alt="👌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Not too noisy and and not too big, but enough to catch my attention <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f514.png" alt="🔔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Its a small function added to <em>.zshrc</em>. It captures the last command that was run and compare it to git (that part can of course be omitted or changed to any other program). In case of error exit from git, the banner is shown</p>



<pre class="wp-block-code"><code>print_error_box() {
    local msg=" $1 "
    local width=${#msg}
    local border

    border=$(printf '%*s' "$width" '')
    border=${border// /─}

    print -P "%F{red}┌${border}┐%f"
    print -P "%F{red}│%f%K{red}%F{white}${msg}%f%k%F{red}│%f"
    print -P "%F{red}└${border}┘%f"
}

typeset -g LAST_CMD=""

preexec() {
    LAST_CMD="$1"
}

precmd() {
    local exit_code=$?
    if (( exit_code != 0 )) &amp;&amp; &#91;&#91; "$LAST_CMD" == git* ]]; then
        print_error_box "GURU MEDITATION ERROR"
    fi
}
</code></pre>
<p>The post <a href="https://monzool.net/blog/2026/04/10/error-banner-in-shell/">Error banner in shell</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://monzool.net/blog/2026/04/10/error-banner-in-shell/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Custom git merge driver</title>
		<link>https://monzool.net/blog/2026/03/24/custom-git-merge-driver/</link>
					<comments>https://monzool.net/blog/2026/03/24/custom-git-merge-driver/#respond</comments>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Tue, 24 Mar 2026 15:28:11 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Source Control]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=930</guid>

					<description><![CDATA[<p>MERGE CONFLICTS IN git are a normal part of collaboration. But, sometimes you end up in situations of solving the same trivial conflict again and again. That was my situation at $WORK on a busy GitHub Actions pipeline project. The problem GitHub recommends pinning actions to full commit SHAs for [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2026/03/24/custom-git-merge-driver/">Custom git merge driver</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>MERGE CONFLICTS IN</strong> git are a normal part of collaboration. But, sometimes you end up in situations of solving the same trivial conflict again and again. That was my situation at $WORK on a busy GitHub Actions pipeline project.</p>



<h2 class="wp-block-heading" id="the-problem">The problem</h2>



<p>GitHub recommends <a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions">pinning actions to full commit SHAs</a> for security. We also apply this practice to our own workflows and action for reproducebility. In practice that means that workflow files are full of lines like this:</p>



<pre class="wp-block-code"><code>uses: Internal-actions/utilities/.github/actions/read-project-file@1028f1aedf5426726c149b3bdfca0b68bf001567 # v1.0.12
</code></pre>



<p>When a colleague bumps a versions, git simply cannot pick one by itself, thus every merge produces conflicts. Every. Single. Time. The resolution is always the same: <strong>always pick whichever side has the higher version</strong>.</p>



<p>After manually resolving the same type of conflict for the umpteenth time, I went looking for a better way.</p>



<h2 class="wp-block-heading" id="git-merge-drivers">Git merge drivers</h2>



<p>It turns out git has a built-in mechanism for exactly this kind of thing: <a href="https://git-scm.com/docs/gitattributes#_defining_a_custom_merge_driver">custom merge drivers</a>. A merge driver is a program that git calls to resolve conflicts on specific files, instead of using its default three-way merge.</p>



<p>The concept is simple. Two pieces of configuration work together:</p>



<ol class="wp-block-list">
<li><strong><code>.gitattributes</code></strong> maps file patterns to a named driver</li>



<li><strong><code>.gitconfig</code></strong> defines calling of that named driver</li>
</ol>



<p>For example, telling git to use a driver called <code>latest-uses-version</code> for workflow files, first add a file matching rule in <em>.gitattributes</em></p>



<pre class="wp-block-code"><code>.github/workflows/*.yml    merge=latest-uses-version
</code></pre>



<p>Then define the driver by name and how it should be called in <em>.gitconfig</em></p>



<pre class="wp-block-code"><code>git config merge.latest-uses-version.name "Pick latest uses version"
git config merge.latest-uses-version.driver "python3 merge-latest-uses-version.py %O %A %B"
</code></pre>



<p>When git encounters a conflict in a matching file, it will call the driver with three temporary files: <code>%O</code> (common ancestor), <code>%A</code> (ours), and <code>%B</code> (theirs). The driver writes its result into <code>%A</code> and exits <code>0</code> for success or <code>1</code> if conflicts remain.</p>



<h2 class="wp-block-heading" id="the-driver">The driver</h2>



<p>I wrote a Python script to handle the <code>uses</code>  version conflicts. In our setup, every <code>uses</code> line is sha-pinned <em>and</em> always has a semver comment appended (ensured by another automation script), like <code>@abc123... # v1.0.12</code>. That comment is what makes it possible for the driver to compare the two sides of a conflict and pick the newer version.</p>



<pre class="wp-block-code"><code>#!/usr/bin/env python3

import re
import subprocess
import sys

ANCESTOR = sys.argv&#91;1]
OURS = sys.argv&#91;2]
THEIRS = sys.argv&#91;3]

# Attempt standard merge first
result = subprocess.run(
    &#91;"git", "merge-file", "-p", OURS, ANCESTOR, THEIRS],
    capture_output=True,
    text=True,
)

if result.returncode == 0:
    # Clean merge — no conflicts
    with open(OURS, "w") as f:
        f.write(result.stdout)
    sys.exit(0)

# Conflicts exist — write the conflicted output, then resolve what we can
content = result.stdout

# Pattern: uses: some/path@&lt;exactly 40 hex chars&gt; # vX.Y.Z
SHA_VERSION_RE = re.compile(
    r"uses:\s+\S+@&#91;0-9a-fA-F]{40}\s+#\s*v(\d+\.\d+\.\d+)"
)

CONFLICT_RE = re.compile(
    r"&lt;&lt;&lt;&lt;&lt;&lt;&lt; &#91;^\n]*\n(.*?)\n=======\n(.*?)\n&gt;&gt;&gt;&gt;&gt;&gt;&gt; &#91;^\n]*",
    re.DOTALL,
)


def pick_latest(match):
    head_block = match.group(1)
    incoming_block = match.group(2)

    head_ver = SHA_VERSION_RE.search(head_block)
    incoming_ver = SHA_VERSION_RE.search(incoming_block)

    # Only resolve if BOTH sides are sha-pinned uses: lines with version comments
    if not head_ver or not incoming_ver:
        return match.group(0)  # Leave conflict markers for manual resolution

    h = tuple(int(x) for x in head_ver.group(1).split("."))
    i = tuple(int(x) for x in incoming_ver.group(1).split("."))

    return head_block if h &gt;= i else incoming_block


resolved = CONFLICT_RE.sub(pick_latest, content)

with open(OURS, "w") as f:
    f.write(resolved)

sys.exit(1 if "&lt;&lt;&lt;&lt;&lt;&lt;&lt; " in resolved else 0)
</code></pre>



<p>It first runs <code>git merge-file</code> to get the standard three-way merge. If conflicts remain, it scans each hunk for the sha-pinned version pattern. Only when <em>both</em> sides match does it pick the higher semver. Anything else is left with conflict markers for manual resolution.</p>



<h2 class="wp-block-heading" id="setting-it-up">Setting it up</h2>



<p>Git merge drivers need two things configured: the driver definition and the file pattern mapping.</p>



<p>The <strong>driver definition</strong> goes in git config &#8211; either globally (<code>~/.gitconfig</code>) or per-repo (<code>.git/config</code>). As our pipeline is spread across multiple repositories I did not want to maintain the same setup in all repositories. By doing the global configuration the setup can be shared between all. Choose a per-repo when the driver is specific to one project.</p>



<p>The <strong>file pattern mapping</strong> goes in <code>.gitattributes</code> &#8211; either a global file (pointed to by <code>core.attributesFile</code>) or a per-repo <code>.gitattributes</code>. The global and per-repo attributes are additive; git merges them together, so they won&#8217;t conflict.</p>



<p>To automate this setup for coworkers, I made a script to do the git setup. </p>



<pre class="wp-block-code"><code>./setup-merge-driver.sh
</code></pre>



<p><em>setup-merge-driver.sh</em>:</p>



<pre class="wp-block-code"><code>#!/usr/bin/bash

# Install the "latest-uses-version" custom merge driver globally.

set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE&#91;0]}")" &amp;&amp; pwd)"
driver_path="${script_dir}/tools/&#x2699;/merge-latest-uses-version.py"

if &#91;&#91; ! -f "${driver_path}" ]]; then
    echo "ERROR: merge driver not found at ${driver_path}" &gt;&amp;2
    exit 1
fi

# Register the merge driver in global git config (idempotent).
git config --global merge.latest-uses-version.name \
    "Auto-resolve sha-pinned action version conflicts by picking the latest"
git config --global merge.latest-uses-version.driver \
    "python3 '${driver_path}' %O %A %B"

echo "&#x2714; Merge driver 'latest-uses-version' registered in global git config."

# Ensure a global attributes file is configured.
attr_file="$(git config --global core.attributesFile 2&gt;/dev/null || true)"

if &#91;&#91; -z "${attr_file}" ]]; then
    attr_file="${HOME}/.gitattributes"
    git config --global core.attributesFile "${attr_file}"
    echo "&#x2714; Global core.attributesFile set to ${attr_file}"
fi

# Expand ~ in case the user configured it that way.
attr_file="${attr_file/#\~/${HOME}}"

# Add patterns (idempotent).
patterns=(
    ".github/workflows/*.yml merge=latest-uses-version"
    "common/*.yml merge=latest-uses-version"
)

touch "${attr_file}"

for pattern in "${patterns&#91;@]}"; do
    if ! grep -qxF "${pattern}" "${attr_file}"; then
        echo "${pattern}" &gt;&gt; "${attr_file}"
        echo "&#x2714; Added to ${attr_file}: ${pattern}"
    else
        echo "· Already present in ${attr_file}: ${pattern}"
    fi
done

echo ""
echo "Done. The merge driver is ready for all repositories."
</code></pre>



<h3 class="wp-block-heading" id="what-it-does">What it does</h3>



<ul class="wp-block-list">
<li>Registers the <code>latest-uses-version</code> driver in global <code>~/.gitconfig</code> (this is idempotent and safe to re-run)</li>



<li>Ensures <code>core.attributesFile</code> is set. It respects an existing one and creates <code>~/.gitattributes</code> only if none is already configured</li>



<li>Adds file patterns (<code>.github/workflows/*.yml</code> and <code>common/*.yml</code>) &#8211; skips if already present</li>



<li>Uses absolute path to the driver script so it works from any repo</li>
</ul>



<h2 class="wp-block-heading" id="the-result">The result</h2>



<p>With the driver installed, what used to be a manual chore now just works. Given a conflict like this during merge:</p>



<pre class="wp-block-code"><code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
      uses: Internal-actions/utilities/.github/actions/read-project-file@1028f1aedf5426726c149b3bdfca0b68bf001567 # v1.0.12
=======
      uses: Internal-actions/utilities/.github/actions/read-project-file@386f499a1f68c20a15df7ad877b77fa139d7e932 # v1.0.15
&gt;&gt;&gt;&gt;&gt;&gt;&gt; feature-branch
</code></pre>



<p>The driver picks <code>v1.0.15</code> and the merge completes cleanly:</p>



<pre class="wp-block-code"><code>      uses: Internal-actions/utilities/.github/actions/read-project-file@386f499a1f68c20a15df7ad877b77fa139d7e932 # v1.0.15
</code></pre>



<p>No manual intervention needed. Git now just does the right thing <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9d9.png" alt="🧙" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br></p>



<h2 class="wp-block-heading" id="multiple-drivers-in-git-config">Multiple drivers in git config</h2>



<p>As a note, then the <code>uses</code> version driver is just one driver. Git supports as many custom merge drivers as as desired</p>



<pre class="wp-block-code"><code># Driver 1: sha-pinned action versions
git config merge.latest-uses-version.name "Pick latest sha-pinned action version"
git config merge.latest-uses-version.driver "python3 scripts/merge-latest-uses-version.py %O %A %B"

# Driver 2: hypothetical package-lock resolver
git config merge.package-lock.name "Regenerate package-lock on conflict"
git config merge.package-lock.driver "scripts/merge-package-lock.sh %O %A %B"

# Driver 3: always keep ours for generated files
git config merge.keep-ours.name "Always keep our version"
git config merge.keep-ours.driver "true"
</code></pre>



<h3 class="wp-block-heading" id="gitattributes-routes-files-to-drivers"><code>.gitattributes</code> routes files to drivers</h3>



<pre class="wp-block-code"><code>.github/workflows/*.yml     merge=latest-uses-version
package-lock.json           merge=package-lock
generated/**                merge=keep-ours
</code></pre>



<h3 class="wp-block-heading" id="rules">Rules</h3>



<ul class="wp-block-list">
<li>Each file matches <strong>at most one</strong> <code>merge=</code> attribute (last match wins, like <code>.gitignore</code>).</li>



<li>If a file matches no <code>merge=</code> rule, git uses its built-in merge.</li>



<li>If you need <strong>two strategies on the same file</strong> (e.g., resolve <code>uses</code> versions <em>and</em> something else in the same YAML), that must be handled within a single driver script. Unfortunately drivers can&#8217;t be chained.</li>



<li>You can use any <code>.gitattributes</code> pattern: exact paths, globs, directory prefixes, etc.</li>
</ul>



<h2 class="wp-block-heading" id="conclusion">Conclusion</h2>



<p>I am super stoked about the result. The algorithm for this particular case was pretty straight forward, but it works just perfectly &#8211; and I got to learn another cool git feature.</p>
<p>The post <a href="https://monzool.net/blog/2026/03/24/custom-git-merge-driver/">Custom git merge driver</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://monzool.net/blog/2026/03/24/custom-git-merge-driver/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Homebrew breaking git tab completion in zsh</title>
		<link>https://monzool.net/blog/2026/03/19/homebrew-breaking-git-tab-completion-in-zsh/</link>
					<comments>https://monzool.net/blog/2026/03/19/homebrew-breaking-git-tab-completion-in-zsh/#respond</comments>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Thu, 19 Mar 2026 20:04:10 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[homebrew]]></category>
		<category><![CDATA[zsh]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=921</guid>

					<description><![CDATA[<p>GIT TAB COMPLETION in zsh suddenly stopped listing my aliases. No git ls, no git tags-ls &#8211; only built-in commands. Worse, commands beyond the most common ones lost their help descriptions entirely. git re&#60;tab&#62; would show rebase, reset, restore with nice descriptions, but git rep&#60;tab&#62; fell through to a bare [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2026/03/19/homebrew-breaking-git-tab-completion-in-zsh/">Homebrew breaking git tab completion in zsh</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>GIT TAB COMPLETION</strong> in zsh suddenly stopped listing my aliases. No <code>git ls</code>, no <code>git tags-ls</code> &#8211; only built-in commands. Worse, commands beyond the most common ones lost their help descriptions entirely. <code>git re&lt;tab&gt;</code> would show <code>rebase</code>, <code>reset</code>, <code>restore</code> with nice descriptions, but <code>git rep&lt;tab&gt;</code> fell through to a bare list of names with no context. Something was clearly off.</p>



<h2 class="wp-block-heading" id="what-happened">What happened</h2>



<p>I am not really sure why this situation ended up happening. It might have been a brew update or a oh-my-zsh update?</p>



<p>Digging into zsh&#8217;s <code>fpath</code>, I discovered that <a href="https://brew.sh/">Homebrew</a> ships its own <code>_git</code> zsh completion wrapper alongside the git formula. It lives at:</p>



<pre class="wp-block-code"><code>/home/linuxbrew/.linuxbrew/share/zsh/site-functions/_git
</code></pre>



<p>This is a lightweight wrapper that narrows down known git commands, to a finite set of &#8220;common commands&#8221; &#8211; things like <code>add</code>, <code>commit</code>, <code>push</code>. </p>



<p>My Ubuntu&#8217;s own <code>_git</code> at <code>/usr/share/zsh/functions/Completion/Unix/_git</code> is far more comprehensive. It has descriptions for all commands and proper alias support.</p>



<h2 class="wp-block-heading" id="figuring-out-which-completion-is-active">Figuring out which completion is active</h2>



<p>To check which <code>_git</code> zsh is actually loading, start a new shell and run:</p>



<pre class="wp-block-code"><code>❱ print -l $fpath
/home/linuxbrew/.linuxbrew/share/zsh/site-functions   # &lt;-- brew
/home/monzool/.oh-my-zsh/plugins/git
...
/usr/share/zsh/functions/Completion/Unix              # &lt;-- system
</code></pre>



<p>zsh resolves autoloaded completion functions by searching <code>fpath</code> <strong>in order</strong> and using the first match. Brew&#8217;s <code>_git</code> is found before the system one because <code>brew shellenv</code> <strong>prepends</strong> its directory to <code>fpath</code>:</p>



<pre class="wp-block-code"><code>fpath&#91;1,0]="/home/linuxbrew/.linuxbrew/share/zsh/site-functions";
</code></pre>



<h2 class="wp-block-heading" id="the-fix">The fix</h2>



<p>The brew site <a href="https://docs.brew.sh/Shell-Completion">documents</a> how to activate its shell completions. In this case I wanted to have the system completions to take precedence.</p>



<p>The fix is to move brew&#8217;s <code>site-functions</code> to the <strong>end</strong> of <code>fpath</code>, so system completions take priority while brew-only completions (like <code>_brew</code>, <code>_rg</code>, <code>_jj</code>) still work.</p>



<p>I added this to <code>~/.zshrc</code> right after <code>eval "$(brew shellenv)"</code>:</p>



<pre class="wp-block-code"><code># Move brew's zsh completions to end of fpath so system completions take priority
fpath=(${fpath:#$HOMEBREW_PREFIX/share/zsh/site-functions} $HOMEBREW_PREFIX/share/zsh/site-functions)
</code></pre>



<p>Then cleared the completion cache</p>



<pre class="wp-block-code"><code>〉rm -f ~/.zcompdump*
〉compinit
</code></pre>



<p>Full git completion restored. Aliases and descriptions for all commands now works <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f973.png" alt="🥳" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a href="https://monzool.net/blog/2026/03/19/homebrew-breaking-git-tab-completion-in-zsh/">Homebrew breaking git tab completion in zsh</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://monzool.net/blog/2026/03/19/homebrew-breaking-git-tab-completion-in-zsh/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Moving to a new android phone sucks</title>
		<link>https://monzool.net/blog/2025/09/19/moving-to-new-a-android-phone-sucks/</link>
					<comments>https://monzool.net/blog/2025/09/19/moving-to-new-a-android-phone-sucks/#respond</comments>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Fri, 19 Sep 2025 13:56:29 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Reviews]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=913</guid>

					<description><![CDATA[<p>SWITCHING ANDROID PHONES is something all Android phone users eventually will need to do. Since invention of the smart phone, more and more of daily life has progressed to be backed by an app. Be it public transportation, car parking, grocery shopping, banking, listening to music or streaming videos. It [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2025/09/19/moving-to-new-a-android-phone-sucks/">Moving to a new android phone sucks</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>SWITCHING ANDROID PHONES</strong> is something all Android phone users eventually will need to do. Since invention of the smart phone, more and more of daily life has progressed to be backed by an app. Be it public transportation, car parking, grocery shopping, banking, listening to music or streaming videos. It all adds up and the longer time you have been using a smart phone, the more stuff do you want to bring with you when switching to a new phone.</p>



<h2 class="wp-block-heading" id="tldr">tl;dr</h2>



<p>Switching phones on Android sucks, it’s just a miserable experience. Okay, maybe a bit over-dramatic, but it genuinely is a lackluster experience. With a few exceptions all finely tuned settings are lost on both the Android system and native apps. Some native apps supports backup/restore, but its a proprietary and mainly manual solution that I expect exactly zero normies would ever mange to complete. </p>



<p><strong>What Android is missing is an integrated backup/restore system and transparent cloud storing (or syncing) of app and system settings</strong>.</p>



<p></p>



<h2 class="wp-block-heading" id="from-to">From 🢡 to</h2>



<p>My Pixel 7a has been a good experience, but ultimately the catastrophic inconsistency in photos made we throw in the towel. I instead acquired a Samsung Galaxy S23. For transferring data from my Pixel phone to the Samsung phone I used the Samsung SmartSwitch app. Android can install all apps itself when switching to a new phone, but all the other files like notification sounds, emulator files, documents etc are left behind. Samsung SmartSwitch will copy all files over</p>



<h2 class="wp-block-heading" id="nova-launcher">Nova Launcher</h2>



<p>So I don’t quite know where to throw blame her. After using the Samsung SmartSwitch functionality, Nova Launcher was installed. But after importing my backup not all settings was restored. This was due to Nova Launcher Premium was not installed in the transfer. After installing that from Google Play &#8211; and restarting the phone, then after another restore, all settings worked. The widgets was missing. Same goes for the wallpaper… so only a partial success.</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆</p>



<h2 class="wp-block-heading" id="system-setup">System setup</h2>



<h3 class="wp-block-heading" id="language">Language</h3>



<p>I run my Android in <code>English(Danish)</code> language setting. For some apps it makes more sense to switch from default english to native danish. Some apps support doing this through the Android settings, which is a really nice feature. Unfortunately not all apps (particularly public transportation apps) support this and you have to poke around in the individual app settings to find where to flip the language</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>



<h3 class="wp-block-heading" id="notification-sounds">Notification sounds</h3>



<p>This one is a weird weird. For many apps I had to use their proprietary backup/restore solution. But for every single one, the notification sound setting was not restored and had reverted to default sounds. All notification and ring-tone files were replicated by Samsung SmartSwitch to be at the exact same place in the filesystem, but it did not work. This make me think that notification sounds are part of the Android OS settings</p>



<p>Perhaps this is a difference between Samsung Android and Pixel Android, but not all notifications are no longer to be found in the various apps. Thanks to the Samsung SmartSwitch app, the files are there in the same location as on the old phone. They are just not all presented. Seems a bit inconsistent between apps, so not really sure what is going on there…</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>



<h2 class="wp-block-heading" id="app-notifications">App notifications</h2>



<p>I had a lot of app restricted on which notifications they were allowed to disturb with. None of these setting ported over, so a horde of attention seeking notifications jingled the phone the first few days until I got the throttle back on that</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>



<h2 class="wp-block-heading" id="google-apps">Google apps</h2>



<p>The Samsung SmartSwitch app filtered out quite a few of the installed Google apps. Gmail, Lens and a few others were transferred, but these were missing:</p>



<ul class="wp-block-list">
<li>Calendar</li>



<li>Files</li>



<li>Clock</li>



<li>Contacts</li>



<li>Phone</li>



<li>GBoard</li>
</ul>



<p>I suspect this is because Samsung has their own competing apps, and blocked the install. What a shit move, Samsung <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a9.png" alt="💩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Weird thing was, that the icons from the apps were actually present. First time activating them, I would then be notified that: the app was not installed, but I could search for them in an app store. After install I realized that none of the settings was restored. Every app had to be set up again from scratch. I don’t know if this was because of the shitty move by Samsung SmartSwitch or just Google not caring…</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>



<h3 class="wp-block-heading" id="google-chrome">Google Chrome</h3>



<p>Chrome have a feature (if <a href="https://support.google.com/chrome/answer/165139?hl=en&amp;co=GENIE.Platform%3DDesktop">sync</a> is enabled), that when opening Chrome on the new phone it will ask if to open all the tabs that are synced. It asked to sync only 124 tabs. Where were all the rest! It turns out it does not sync inactive tabs <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f926.png" alt="🤦" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Chrome has a feature where tabs that has not be active for a set time (default 21 days) the tab will become an inactive tab. I had to disable the inactive tab feature, then wait a bit, then wipe Chrome app data on the new phone. Now when opening, all 463 tabs opened in the new phone</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆</p>



<h3 class="wp-block-heading" id="google-bgoard">Google BGoard</h3>



<p>As mentioned many of the Google apps was not transferred and had to be set up from scratch. I’ve made quite a few changes to GBoard on the old phone. I could only find a way to backup the personal dictionary though. Not that it mattered, because I could not find any way to restore the dictionary on the new phone. Why are these settings not just stored in the cloud?</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>



<h3 class="wp-block-heading" id="google-weather">Google Weather</h3>



<p>On the new phone the weather app was not there. It was not to be found. I then searched the Play store but could not find the Google Weather app (the app store is littered with weather apps &#8211; many of questionable intent). Then one day, at a seeming arbitrary point it popped up a notification about needing precise location permissions. So it <strong>was</strong> installed. I then looked at the apps list in settings. I probably should have looked there, but didn’t think of it as I couldn’t find it the app drawer. In the settings I found this setting: <code>Show Weather on Apps screen</code>. After enabling that, the app could suddenly be found and started manually. What an obnoxious setting. I want to see the weather to plan ahead &#8211; not when this brain-dead app wrongly thinks its relevant. A weird this is that on the new phone it thinks the time is one hour later that it actually is. I ended up deleting the app</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>



<h2 class="wp-block-heading" id="other-apps">Other apps</h2>



<h3 class="wp-block-heading" id="signal">Signal</h3>



<p>Signal was nice. You basically just select transfer on the old and new phone, and the transfer happens automagically <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9d9.png" alt="🧙" class="wp-smiley" style="height: 1em; max-height: 1em;" /> I do wonder how this process will be if you have lost your old phone <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>



<h3 class="wp-block-heading" id="whatsapp">Whatsapp</h3>



<p>Except for the notification sound everything was restored automatically after login. It makes backup/restore to Google Drive. Works perfectly</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<h3 class="wp-block-heading" id="microsoft-authenticator">Microsoft Authenticator</h3>



<p>The Microsoft Authenticator has a built-in cloud solution to do automatic backup and manual restore to another phone. However, restoring was not working. It kept saying <em>“Your backup is not stored with this email”</em></p>



<p>“Fortunately” this is a common bug, that <a href="https://learn.microsoft.com/en-us/answers/questions/955233/microsoft-authenticator-unable-to-recover-your-bac">was complained about before</a> had a solution for (Microsoft incompetently do not provide linking to answers, but look for the answer by <strong>Arif IT-Pro</strong>). An authenticator app that cannot restore do not bring confidence…</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆</p>



<h3 class="wp-block-heading" id="ok-benzin">OK Benzin</h3>



<p>This was a particular annoying experience, partly because I had forgotten my password. Their reset procedure was to send a reset link in an email. That link would open the app, but the app would go to normal login, not password reset. Had to use a computer and their website to get my password reset <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f926.png" alt="🤦" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆</p>



<h3 class="wp-block-heading" id="amdroid">AMDroid</h3>



<p>Moving an alarm app (AMdroid) requires three system setting changes</p>



<ol class="wp-block-list">
<li>Add it to the <em>never sleeping apps</em> list</li>



<li>Set it to <em>unrestricted battery</em> usage</li>



<li>Allow it to <em>appear on top</em></li>
</ol>



<p>By bad luck I forgot to do the manual backup/restore for that app, so I overslept the next workday.</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆</p>



<h3 class="wp-block-heading" id="f-droid">F-Droid</h3>



<p>F-Droid itself seemed to just work. Maybe that is from using the Samsung SmartSwitch, because from reading online that would have required a backup/restore session, but at first glance all apps where transferred <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Later I found that it was actually a mixed story. The apps that had a presence on both F-Droid and Play Store, were now all installed as the Play Store edition?!</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆</p>



<h3 class="wp-block-heading" id="macrodroid">MacroDroid</h3>



<p>Had to do backup/restore, but that worked fine enough. Then I manually had to sideload their permission helper. That required flipping several options to get permission to install the apk. I guess in the future this kind of system tweaking will only become even more complicated. I really hope they manage to keep MacroDroid and option, because its invaluable to un-fuck some of the crap in Android</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆</p>



<h3 class="wp-block-heading" id="dgt-gtd">DGT GTD</h3>



<p>I’ve been using this todo/reminder app for quite a long time, but are not transitioning to Tasks It automatically does a local zip backup of all tasks, and by using Samsung SmartSwitch the backups were available for restoring.</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>



<h2 class="wp-block-heading" id="apps-non-google-with-online-accounts">Apps (non Google) with online accounts</h2>



<p>Many apps like social media (Strava, Reddit, Bluesky, Lemmy etc) rely on online services and only require a login to get up and running again. Apart from throttling on their notifications, surprisingly few issues with any of them</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>



<h2 class="wp-block-heading" id="wall-of-shame">Wall of shame <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a9.png" alt="💩" class="wp-smiley" style="height: 1em; max-height: 1em;" /></h2>



<h2 class="wp-block-heading" id="digital-wellbeing">Digital wellbeing</h2>



<p>What an atrocious system app. This app would endlessly notify me with various well being advice. Seriously, just <strong>shut the <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92c.png" alt="🤬" class="wp-smiley" style="height: 1em; max-height: 1em;" /> uppppppp!!!!!!!!</strong>. The app didn’t even allow me to disable notifications<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /> The toggle button was disabled. I added it to my app kill list <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5e1.png" alt="🗡" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f480.png" alt="💀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> in MacroDroid</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<h2 class="wp-block-heading" id="waze">Waze</h2>



<p>It was copied to the phone just fine and only required a login to work; but as with many other apps, the settings had reset. Initially the notifications from Waze angered me quite a bit. Every day it would notify me that there is 30 minutes until arriving at work. What am I suppose to use that information for? I’ve already been in public transportation for 20 minutes before that, and know exactly when I will arrive at work. A complete pointless notification. The feature <strong>might</strong> have a value if you drive to work in a congested area, and you need to be at work at a precise time, but I think that is a notification you would want to activate for that specific need. Some interweb search and I managed to disable that notification. I generally had to poke a lot of settings in Waze to make it less obnoxious. Waze also got added to my app kill list <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5e1.png" alt="🗡" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f480.png" alt="💀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> in MacroDroid</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f595.png" alt="🖕" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>



<h2 class="wp-block-heading" id="misc">Misc</h2>



<p>My digital drivers license app did not transfer settings. To activate the drivers license I have to go through a validation process that includes scanning of my passport. I am not really too upset about that. It could be a fair security restriction.</p>



<h2 class="wp-block-heading" id="summary">Summary</h2>



<p>An seemingly endless task of logins and permissions acceptances. The system settings app list counted around 350 apps installed, and most of them require some sort of login. I took the most important ones, that I could remember in the moment in one go, and then delayed the rest until I needed them… which is also why I held up the line in the supermarket as I had to login, using my national digital id (MitId), in the supermarket app to get the discounts <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p>Most apps, including many of Google’s had their settings lost. I had to set up language, notifications and a lot of other things once again</p>



<p>Why do Google apps not store all their settings in the cloud? Why don’t <strong>all</strong> apps not store their settings in the cloud. Is there no api for storing settings/backup in Google Drive? There should be. That would make changing phones not so much of a shit show</p>



<p>System settings might be a bit more difficult to depending on vendor and version differences between phones, but its not an unsolvable problem. Like it migrated wifi and bluetooth connection registrations just fine, so why not also migrate hotspot settings, personal dictionaries, app language settings etc. Should be possible</p>



<p>Switching Android phones is a <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a9.png" alt="💩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> experience.</p>
<p>The post <a href="https://monzool.net/blog/2025/09/19/moving-to-new-a-android-phone-sucks/">Moving to a new android phone sucks</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://monzool.net/blog/2025/09/19/moving-to-new-a-android-phone-sucks/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Pixel 7a Trusty TEE failure</title>
		<link>https://monzool.net/blog/2025/05/25/pixel-7a-trusty-tee-failure/</link>
					<comments>https://monzool.net/blog/2025/05/25/pixel-7a-trusty-tee-failure/#respond</comments>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Sun, 25 May 2025 14:07:47 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=905</guid>

					<description><![CDATA[<p>ANDROID TRUSTY TEE is failing on my Pixel 7a (Android 15) and causing my phone to randomly waiting at the SIM pin login in the mornings. This is the short conclusion of my findings, for an issue I currently have with my phone. When waking up at the morning, I [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2025/05/25/pixel-7a-trusty-tee-failure/">Pixel 7a Trusty TEE failure</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>ANDROID TRUSTY TEE</strong> is failing on my Pixel 7a (Android 15) and causing my phone to randomly waiting at the SIM pin login in the mornings. This is the short conclusion of my findings, for an issue I currently have with my phone.</p>



<p>When waking up at the morning, I would find my phone being stuck waiting for my SIM pin login. At the night before nothing extraordinary events had happened. I just put down the phone, plugged in the powercord and went to sleep. As this had happend quite a few times the last couple of months, I decided to investigate even though I an not an Android expert</p>



<p>I started by investigating information from <code>adb logcat</code>.</p>



<pre class="wp-block-code"><code>--------- beginning of kernel
05-25 01:30:15.540     0     0 I trusty  : boot args 0x*** 0x*** 0x6 0x0

&lt;snip&gt;

05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_tee_storage]&#91;gf_tee_open_object] exit. err=GF_ERROR_OPEN_SECURE_OBJECT_FAILED, errno=1035
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_secure_object]&#91;gf_so_load_persistent_object] so(finger_0_3.so) not exist
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_secure_object]&#91;gf_so_load_persistent_object] exit. err=GF_ERROR_OPEN_SECURE_OBJECT_FAILED, errno=1035
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_fpcore_common]&#91;fpcore_load_template_object] exit. err=GF_ERROR_OPEN_SECURE_OBJECT_FAILED, errno=1035
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;I]&#91;gf_tee_storage]&#91;trusty_file_open] 0 LHBM_finger_0_3_bak.so 1
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_tee_storage]&#91;trusty_file_open] Unable to open LHBM_finger_0_3_bak.so on port com.android.trusty.storage.client.td
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_tee_storage]&#91;trusty_file_open] Failed to open LHBM_finger_0_3_bak.so on com.android.trusty.storage.client.td. No fallback available.
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_tee_storage]&#91;gf_tee_open_object] exit. err=GF_ERROR_OPEN_SECURE_OBJECT_FAILED, errno=1035
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_secure_object]&#91;gf_so_load_persistent_object] so(finger_0_3_bak.so) not exist
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_secure_object]&#91;gf_so_load_persistent_object] exit. err=GF_ERROR_OPEN_SECURE_OBJECT_FAILED, errno=1035
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_fpcore_common]&#91;fpcore_load_template_object] exit. err=GF_ERROR_OPEN_SECURE_OBJECT_FAILED, errno=1035
05-25 01:30:46.436     0     0 I trusty  : &#91;GF_TA]&#91;E]&#91;gf_fpcore_common]&#91;fpcore_get_finger_ids_from_so] exit. err=GF_ERROR_OPEN_SECURE_OBJECT_FAILED, errno=1035
05-25 07:56:03.561     0     0 I trusty  : &#91;GF_TA]&#91;I]&#91;gf_tee_storage]&#91;trusty_file_open] 0 LHBM_auth_att.so 0
05-25 07:56:47.350     0     0 I trusty  : sac-ssmt: 111: TPU: Setup TZ SSMT 0
05-25 07:56:47.369     0     0 I trusty  : sac-ssmt: 111: TPU: Setup TZ SSMT 0
05-25 07:56:58.807     0     0 I trusty  : sac-ssmt: 111: TPU: Setup TZ SSMT 0
05-25 07:56:58.876     0     0 I trusty  : sac-ssmt: 111: TPU: Setup TZ SSMT 0
05-25 07:57:56.248     0     0 I trusty  : &#91;GF_TA]&#91;I]&#91;gf_delmar_sensor]&#91;determine_expo_mode] operation: 2, retry: 0, need_dec_gain_due_to_als: 0, expo mode:0</code></pre>



<p>I did a SIM pin unlock <strong>07:56</strong>, but before that the phone had just being doing nothing since <strong>01:30:46</strong> where it had been failing since boot at <strong>01:30:15</strong>.</p>



<p>When feeding the errors messages to ChatGPT. It concluded that this is from a failing <strong>Trusty TEE (Trusted Execution Environment)</strong>. <a href="https://source.android.com/docs/security/features/trusty">Trusty TEE</a> is a security feature that runs isolated from the Android OS, and uses trust zones for handling keys, bio-metrics, pins and other sensitive data.</p>



<p>So if <em>Trusty Tee</em> was the last thing running &#8211; what made it then be needed? I decided to investigate the cause of the reboot. To get access restricted logs, I had to generate and download a bugreport from the phone</p>



<pre class="wp-block-code"><code>❱ adb bugreport reboot_at_night-2025-05-25.zip</code></pre>



<p>Grepping a bit in kernel message log for failures…</p>



<pre class="wp-block-code"><code>❱ grep -a -iE 'panic|watchdog|fatal|reboot|crash|stack|Call trace' ./FS/data/misc/recovery/last_kmsg</code></pre>



<p>revealed some interesting information</p>



<pre class="wp-block-code"><code>&#91;474014.231799]&#91;  T547] init: Received sys.powerctl='reboot,unAttended,mainline_update' from pid: 1776 (system_server)
&#91;474014.232080]&#91;    T1] init: Got shutdown_command 'reboot(unattended,mainline_update)'...
&#91;474016.354876]&#91;    T1] init: Service vold has 'reboot_on_failure' option and failed, shutting down System.
&#91;474020.389800]&#91;    T1] init: Reboot ending, jumping to kernel
...
Reboot Info:
  Reboot reason: 0xcafe - User Reboot(S/W Reboot)
  Reboot mode: 0x0 - Normal Boot</code></pre>



<p>So the device has rebooted due to an <em>unattended mainline</em> update. Unattended updates is a new feature introduced in Android 14 that allows the Android system to automatically apply certain system updates (mainline updates) and then start the phone again, such that the user will not know that the update happened (apart from all background apps being closed).</p>



<p>I then grepped for updates from today, but the list was surprisingly long, so narrowed it down to mainline updates close to the reboot timestamp</p>



<pre class="wp-block-code"><code>❱ adb shell dumpsys package apex | grep -E 'Package \&#91;|lastUpdateTime' | grep -A1  mainline |  grep -B1 2025-05-25
  Package &#91;com.google.mainline.telemetry] (17fc754):
    lastUpdateTime=2025-05-25 01:30:43
  Package &#91;com.google.mainline.adservices] (df25ac):
    lastUpdateTime=2025-05-25 01:30:43</code></pre>



<p>Two mainline updates! That looks like the smoking gun.</p>



<p>Reading up a bit on the unattended updating mechanism, I believe the story is:</p>



<ul class="wp-block-list">
<li>A nightly automatic update of apps included a mainline update that required rebooting (<code>com.google.mainline.adservices</code>).</li>



<li>The phone is issuing an unattended reboot (<code>sys.powerctl='reboot,unattended,mainline_updated'</code>).</li>



<li>When booting the system will use <a href="https://source.android.com/docs/core/ota/resume-on-reboot"><em>Resume on Reboot&#8221;</em></a><em> to do <a href="https://source.android.com/docs/core/ota/resume-on-reboot#sim-pin-replay"></a></em><a href="https://source.android.com/docs/core/ota/resume-on-reboot#sim-pin-replay">Sim Pin Replay*</a> to do SIM pin login on behalf of the user.</li>



<li><em>Resume on reboot</em> will use <em>Trusty TEE</em> to do the critical trust action of doing SIM PIN login.</li>



<li><em>Trusty TEE</em> fails to access the fingerprint storage (<code>Unable to open LHBM_finger_0_3_bak.so</code>), which I presume is required for validating access to SIM pin data and the sequence stops prematurely</li>
</ul>



<p>This is a particular annoying issue as the phone will fail to reboot at any arbitrary night. It fully depends on when Google decides to update a mainline app.</p>



<p>Not sure how to fix this… I guess the error is related to the fingerprint storage, so perhaps either switching to another unlock method (which I am not interested in), or clearing current stored fingerprints and re-create. Regardless there could be long time for a verification as this depends on when Google pushes the next mainline update <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937.png" alt="🤷" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a href="https://monzool.net/blog/2025/05/25/pixel-7a-trusty-tee-failure/">Pixel 7a Trusty TEE failure</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://monzool.net/blog/2025/05/25/pixel-7a-trusty-tee-failure/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ESET crashing with locale error</title>
		<link>https://monzool.net/blog/2025/04/24/eset-crashing-with-locale-error/</link>
					<comments>https://monzool.net/blog/2025/04/24/eset-crashing-with-locale-error/#respond</comments>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Thu, 24 Apr 2025 08:41:41 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Software]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=899</guid>

					<description><![CDATA[<p>ESET RUNTIME ERROR because of locale settings. That is what I by coincident discovered one day, when looking at the Ubuntu syslog for other things. ESET is a malware protection software that is bestowed upon us by $WORK. It works on Windows, Mac and Linux and usually it doesn’t make [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2025/04/24/eset-crashing-with-locale-error/">ESET crashing with locale error</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p></p>



<p><strong>ESET RUNTIME ERROR</strong> because of locale settings. That is what I by coincident discovered one day, when looking at the Ubuntu syslog for other things. <a href="https://www.eset.com/">ESET</a> is a malware protection software that is bestowed upon us by $WORK. It works on Windows, Mac and Linux and usually it doesn’t make much fuzz.</p>



<p>The syslog was filled with “endless” entries of runtime errors due to failure in locale initialization:</p>



<pre class="wp-block-code"><code><a href="#cb1-1"></a>root@L5:~# tail -f /var/log/syslog
<a href="#cb1-2"></a>2025-04-22T06:46:16.404457+02:00 L5 egui&#91;9000]: Locale not supported by C library.#012#011Using the fallback 'C' locale.
<a href="#cb1-3"></a>2025-04-22T06:46:16.901839+02:00 L5 eset.eea&#91;9000]: terminate called after throwing an instance of 'std::runtime_error'
<a href="#cb1-4"></a>2025-04-22T06:46:16.902010+02:00 L5 eset.eea&#91;9000]:   what():  locale::facet::_S_create_c_locale name not valid
<a href="#cb1-5"></a>2025-04-22T06:46:17.573264+02:00 L5 eset.eea&#91;4139]: Aborted (core dumped)</code></pre>



<p>First question might be: “Why are you poking around in the syslog when we modern linux folks have journald?”. It’s quite possibly habit, but I just find it easier to open the syslog file at browse around for stuff</p>



<p><strong>Sidenote:</strong> journald</p>



<p>I have yet to find a good way to <strong>discover</strong> these kind of issues with <code>journalctl</code>. There is <code>journalctl -p err..alert -x --boot</code> but for this case none of the messages can be found with that filter! Also, annoyingly, the core dump errors are listed as owned by the <code>systemd-coredump</code> service, and not the <code>eea.service</code> (for reasons discovered later there is a reason for that in that case).</p>



<p>Then having already discovered a suspicious message, does not always make it straight forward to investigate further.</p>



<pre class="wp-block-code"><code><a href="#cb2-1"></a>❱ journalctl --since "2 days ago" -g 'locale\:\:facet\:\:_S_create_c_locale'
<a href="#cb2-2"></a>Apr 22 06:41:59 L5 eset.eea&#91;4204]:   what():  locale::facet::_S_create_c_locale name not valid</code></pre>



<p>That entry told us that it originated from <code>eset.eee</code>, but as that is not part of the <code>MESSAGE</code>, grepping for it</p>



<pre class="wp-block-code"><code><a href="#cb3-1"></a>❱ journalctl --since "2 days ago" -g 'eset.eea'</code></pre>



<p>will yield no result. Instead we can attempt to filter on the script name</p>



<pre class="wp-block-code"><code><a href="#cb4-1"></a>❱ journalctl --since "2 days ago" _COMM=eset.eea</code></pre>



<p>which also yields no results. Then using the syslog identifier finally gives a result</p>



<pre class="wp-block-code"><code><a href="#cb5-1"></a>❱ journalctl --since "2 days ago" SYSLOG_IDENTIFIER=eset.eea
<a href="#cb5-2"></a>Apr 22 06:41:59 L5 eset.eea&#91;4204]: terminate called after throwing an instance of 'std::runtime_error'
<a href="#cb5-3"></a>Apr 22 06:41:59 L5 eset.eea&#91;4204]:   what():  locale::facet::_S_create_c_locale name not valid
<a href="#cb5-4"></a>Apr 22 06:42:00 L5 eset.eea&#91;4139]: Aborted (core dumped)</code></pre>



<p>Ignoring the rotated syslog files, then there is a lot of entries of that error</p>



<pre class="wp-block-code"><code><a href="#cb6-1"></a>root@L5:~# grep "locale::facet::_S_create_c_locale" /var/log/syslog | wc -l
<a href="#cb6-2"></a>2750</code></pre>



<p>The current syslog file ranges 13 days of errors (using <code>sed</code> to print only first and last hits)</p>



<pre class="wp-block-code"><code><a href="#cb7-1"></a>root@L5:~# grep "locale::facet::_S_create_c_locale" /var/log/syslog | sed -n '1p;$p'
<a href="#cb7-2"></a>2025-04-10T06:44:53.808725+02:00 L5 eset.eea&#91;1227413]:   what():  locale::facet::_S_create_c_locale name not valid
<a href="#cb7-3"></a>2025-04-22T09:51:36.491258+02:00 L5 eset.eea&#91;42077]:   what():  locale::facet::_S_create_c_locale name not valid</code></pre>



<p>The error let me to check the core dump registry</p>



<pre class="wp-block-code"><code><a href="#cb8-1"></a>root@L5:~# coredumpctl | head
<a href="#cb8-2"></a>TIME                             PID  UID  GID SIG     COREFILE EXE                                                                                                              SIZE
<a href="#cb8-3"></a>Mon 2025-03-17 07:58:38 CET  4065929 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-4"></a>Mon 2025-03-17 07:59:39 CET  4066715 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-5"></a>Mon 2025-03-17 08:00:40 CET  4067264 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-6"></a>Mon 2025-03-17 08:01:41 CET  4067862 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-7"></a>Mon 2025-03-17 08:02:42 CET  4068615 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-8"></a>Mon 2025-03-17 08:03:43 CET  4074040 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-9"></a>Mon 2025-03-17 08:04:45 CET  4077727 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-10"></a>Mon 2025-03-17 08:05:46 CET  4080214 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui
<a href="#cb8-11"></a>Mon 2025-03-17 08:06:47 CET  4088606 1000 1000 SIGABRT missing  /opt/eset/eea/lib/egui</code></pre>



<p>Well. It sure crashes a lot <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<pre class="wp-block-code"><code><a href="#cb9-1"></a>root@L5:~# ls -1 /var/lib/systemd/coredump/*egui* | wc -l
<a href="#cb9-2"></a>1728
<a href="#cb9-3"></a>
<a href="#cb9-4"></a>root@L5:~# du -chs /var/lib/systemd/coredump/*egui*
<a href="#cb9-5"></a>3.7G    total</code></pre>



<p>Looking more closely to the core dumps, confirms that the error appear to be some locale related stuff on the main thread of the program egui.</p>



<pre class="wp-block-code"><code>root@L5:~# coredumpctl info /opt/eset/eea/lib/egui
       Message: Process 4067264 (egui) of user 1000 dumped core.

                Module libstdc++.so.6 from deb gcc-14-14.2.0-4ubuntu2~24.04.amd64
                Module libsystemd.so.0 from deb systemd-255.4-1ubuntu8.4.amd64
                Module libzstd.so.1 from deb libzstd-1.5.5+dfsg2-2build1.1.amd64
                Module libgcc_s.so.1 from deb gcc-14-14.2.0-4ubuntu2~24.04.amd64
                Module libprotobuf.so.32 without build-id.
                Module libcommon.so without build-id.
                Module egui without build-id.
                Stack trace of thread 4067269:
                #0  0x00007cac3e89eb1c __pthread_kill_implementation (libc.so.6 + 0x9eb1c)
                #1  0x00007cac3e84526e __GI_raise (libc.so.6 + 0x4526e)
                #2  0x00007cac3e8288ff __GI_abort (libc.so.6 + 0x288ff)
                #3  0x00007cac40563623 n/a (libcommon.so + 0x363623)
                #4  0x00007cac40a5223a _ZN10__cxxabiv111__terminateEPFvvE (libcommon.so + 0x85223a)
                #5  0x00007cac40a522a5 _ZSt9terminatev (libcommon.so + 0x8522a5)
                #6  0x00007cac40a523f7 __cxa_throw (libcommon.so + 0x8523f7)
                #7  0x00007cac40564f16 _ZSt21__throw_runtime_errorPKc (libcommon.so + 0x364f16)
                #8  0x00007cac40b07ce8 _ZNSt6locale5facet18_S_create_c_localeERP15__locale_structPKcS2_ (libcommon.so + 0x907ce8)
                #9  0x00005f5d61c8eff8 _ZNSt6locale5_ImplC2EPKcm (egui + 0x8eff8)
                #10 0x00005f5d61c902a6 _ZNSt6localeC1EPKc (egui + 0x902a6)
                #11 0x00005f5d61c87ac8 _ZN9AgentView26ConvertISO8601ToLocaleTimeERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE (egui + 0x87ac8)
                #12 0x00005f5d61c885a3 _ZN9AgentView6UpdateERK17ConfigurationData (egui + 0x885a3)
                #13 0x00005f5d61c82d35 _ZN13WindowManager26ProcessConfigurationChangeERK17ConfigurationData (egui + 0x82d35)
                #14 0x00005f5d61c53fc0 _ZN16GuiThreadManager11ProcessTaskEv (egui + 0x53fc0)
                #15 0x00005f5d61c7f9be _ZN19GtkGuiThreadManager8OnWakeUpEPv (egui + 0x7f9be)
                #16 0x00007cac3f59d48e n/a (libglib-2.0.so.0 + 0x5d48e)
                #17 0x00007cac3f5fc717 n/a (libglib-2.0.so.0 + 0xbc717)
                #18 0x00007cac3f59ca53 g_main_context_iteration (libglib-2.0.so.0 + 0x5ca53)
                #19 0x00007cac3f7d388d g_application_run (libgio-2.0.so.0 + 0xe788d)
                #20 0x00005f5d61c80835 _ZN19GtkGuiThreadManager10RunGuiLoopEv (egui + 0x80835)
                #21 0x00005f5d61c4a749 n/a (egui + 0x4a749)
                #22 0x00005f5d61c90793 n/a (egui + 0x90793)
                #23 0x00007cac3e89ca94 start_thread (libc.so.6 + 0x9ca94)
                #24 0x00007cac3e929c3c __clone3 (libc.so.6 + 0x129c3c</code></pre>



<p>I checked my locale settings, but it seemed fine</p>



<pre class="wp-block-code"><code><a href="#cb11-1"></a>root@L5:~# locale
<a href="#cb11-2"></a>LANG=C.UTF-8
<a href="#cb11-3"></a>LANGUAGE=en_GB
<a href="#cb11-4"></a>LC_CTYPE=C.UTF-8
<a href="#cb11-5"></a>LC_NUMERIC=en_US.UTF-8
<a href="#cb11-6"></a>LC_TIME=en_DK.UTF-8
<a href="#cb11-7"></a>LC_COLLATE=C.UTF-8
<a href="#cb11-8"></a>LC_MONETARY=en_DK.UTF-8
<a href="#cb11-9"></a>LC_MESSAGES=C.UTF-8
<a href="#cb11-10"></a>LC_PAPER=C.UTF-8
<a href="#cb11-11"></a>LC_NAME=en_DK.UTF-8
<a href="#cb11-12"></a>LC_ADDRESS=en_DK.UTF-8
<a href="#cb11-13"></a>LC_TELEPHONE=en_DK.UTF-8
<a href="#cb11-14"></a>LC_MEASUREMENT=en_DK.UTF-8
<a href="#cb11-15"></a>LC_IDENTIFICATION=en_DK.UTF-8
<a href="#cb11-16"></a>LC_ALL=</code></pre>



<p>I then thought to check the environment of the ESET docker service.</p>



<pre class="wp-block-code"><code><a href="#cb12-1"></a>root@L5:~# systemctl show eea.service --property=Environment
<a href="#cb12-2"></a>Environment=</code></pre>



<p>I’m guessing here that the ESET eea.service starts very early and doesn’t get any LANG or LC_ALL passed?</p>



<p>Lets try a workaround this using systemd and poke the service configuration to specify those locale settings specifically for ESET</p>



<pre class="wp-block-code"><code><a href="#cb13-1"></a>root@L5:~# systemctl status eea.service
<a href="#cb13-2"></a>● eea.service - ESET Endpoint Antivirus
<a href="#cb13-3"></a>     Loaded: loaded (/usr/lib/systemd/system/eea.service; enabled; preset: enabled)
<a href="#cb13-4"></a>     Active: active (running) since Tue 2025-04-22 07:38:41 CEST; 1h 5min ago</code></pre>



<p>Patching <em>/usr/lib/systemd/system/eea.service</em></p>



<pre class="wp-block-code"><code><a href="#cb14-1"></a>&#91;Unit]
<a href="#cb14-2"></a>Description=ESET Endpoint Antivirus
<a href="#cb14-3"></a>After=network.target
<a href="#cb14-4"></a>Before=eea_upgrade.service
<a href="#cb14-5"></a>Requires=eea_upgrade.service
<a href="#cb14-6"></a>ConditionPathExists=!/var/opt/eset/eea/updated/app/eea.bin
<a href="#cb14-7"></a>
<a href="#cb14-8"></a>&#91;Service]
<a href="#cb14-9"></a>Type=notify
<a href="#cb14-10"></a>+Environment=LC_ALL=C
<a href="#cb14-11"></a>+Environment=LANG=C
<a href="#cb14-12"></a>ExecStartPre=/opt/eset/eea/lib/install_scripts/check_start.sh
<a href="#cb14-13"></a>ExecStart=/opt/eset/eea/sbin/startd
<a href="#cb14-14"></a>ExecStartPost=-/opt/eset/eea/lib/install_scripts/launch_gui_all_users.sh
<a href="#cb14-15"></a>ExecStopPost=-/usr/bin/killall /opt/eset/eea/lib/egui --quiet
<a href="#cb14-16"></a>KillMode=process
<a href="#cb14-17"></a>Restart=always
<a href="#cb14-18"></a>TimeoutStartSec=180
<a href="#cb14-19"></a>TimeoutStopSec=120
<a href="#cb14-20"></a>OOMScoreAdjust=-800</code></pre>



<p>Reload</p>



<pre class="wp-block-code"><code><a href="#cb15-1"></a>systemctl daemon-reload
<a href="#cb15-2"></a>systemctl restart eea.service</code></pre>



<p>Verifying it is set</p>



<pre class="wp-block-code"><code><a href="#cb16-1"></a>root@L5:~# systemctl show eea.service --property=Environment
<a href="#cb16-2"></a>Environment=LANG=C.UTF-8 LC_ALL=C.UTF-8</code></pre>



<p>Still crashes</p>



<pre class="wp-block-code"><code><a href="#cb17-1"></a>root@L5:~# tail -f /var/log/syslog
<a href="#cb17-2"></a>2025-04-22T09:02:01.799953+02:00 L5 cron&#91;1719]: (*system*eset-eea-vapm) RELOAD (/etc/cron.d/eset-eea-vapm)
<a href="#cb17-3"></a>2025-04-22T09:02:15.027347+02:00 L5 egui&#91;25254]: Locale not supported by C library.#012#011Using the fallback 'C' locale.
<a href="#cb17-4"></a>2025-04-22T09:02:15.526396+02:00 L5 eset.eea&#91;25254]: terminate called after throwing an instance of 'std::runtime_error'
<a href="#cb17-5"></a>2025-04-22T09:02:15.526550+02:00 L5 eset.eea&#91;25254]:   what():  locale::facet::_S_create_c_locale name not valid</code></pre>



<p>Hmm. What starts what?</p>



<pre class="wp-block-code"><code><a href="#cb18-1"></a>root@L5:~# ps -eo pid,ppid,comm,cmd | grep eset
<a href="#cb18-2"></a>   1295       2 nvidia-modeset/ &#91;nvidia-modeset/kthread_q]
<a href="#cb18-3"></a>   1296       2 nvidia-modeset/ &#91;nvidia-modeset/deferred_close_kthread_q]
<a href="#cb18-4"></a>   2202       1 ERAAgent        /opt/eset/RemoteAdministrator/Agent/ERAAgent --daemon --pidfile /var/run/eraagent.pid
<a href="#cb18-5"></a>   2276    2250 Xorg            /usr/lib/xorg/Xorg -nolisten tcp -background none -seat seat0 vt2 -auth /run/sddm/xauth_CRQCla -noreset -displayfd 16
<a href="#cb18-6"></a>   4511    3845 egui_autorestar /bin/sh /opt/eset/eea/lib/install_scripts/egui_autorestart.sh --gapplication-service
<a href="#cb18-7"></a>  22570    3845 egui_autorestar /bin/sh /opt/eset/eea/lib/install_scripts/egui_autorestart.sh --gapplication-service
<a href="#cb18-8"></a>  23898       1 startd          /opt/eset/eea/sbin/startd
<a href="#cb18-9"></a>  23900   23898 logd            /opt/eset/eea/lib/logd
<a href="#cb18-10"></a>  23901   23898 scand           /opt/eset/eea/lib/scand
<a href="#cb18-11"></a>  23902   23898 sysinfod        /opt/eset/eea/lib/sysinfod
<a href="#cb18-12"></a>  23903   23898 updated         /opt/eset/eea/lib/updated
<a href="#cb18-13"></a>  23904   23898 licensed        /opt/eset/eea/lib/licensed
<a href="#cb18-14"></a>  23905   23898 schedd          /opt/eset/eea/lib/schedd
<a href="#cb18-15"></a>  23906   23898 utild           /opt/eset/eea/lib/utild
<a href="#cb18-16"></a>  23908   23898 confd           /opt/eset/eea/lib/confd
<a href="#cb18-17"></a>  23909   23898 econnd          /opt/eset/eea/lib/econnd
<a href="#cb18-18"></a>  23944    3845 egui_autorestar /bin/sh /opt/eset/eea/lib/install_scripts/egui_autorestart.sh --gapplication-service
<a href="#cb18-19"></a>  23958   23898 oaeventd        /opt/eset/eea/lib/oaeventd
<a href="#cb18-20"></a>  24000   23898 wapd            /opt/eset/eea/lib/wapd
<a href="#cb18-21"></a>  24016   23898 execd           /opt/eset/eea/lib/execd
<a href="#cb18-22"></a>  24036   22570 egui            /opt/eset/eea/lib/egui --gapplication-service</code></pre>



<p>There is a ESET daemon called <code>startd</code> but it doesn’t appear to be parent of the egui related script &#8211; that I assume is <em>egui_autorestart.sh</em>.</p>



<p>Oh wait, that script is started from the service from before</p>



<pre class="wp-block-code"><code><a href="#cb19-1"></a>root@L5:~# cat /usr/lib/systemd/system/eea.service
<a href="#cb19-2"></a>&#91;Service]
<a href="#cb19-3"></a>Environment=LANG=C.UTF-8
<a href="#cb19-4"></a>Environment=LC_ALL=C.UTF-8
<a href="#cb19-5"></a>Type=notify
<a href="#cb19-6"></a>ExecStartPre=/opt/eset/eea/lib/install_scripts/check_start.sh
<a href="#cb19-7"></a>ExecStart=/opt/eset/eea/sbin/startd
<a href="#cb19-8"></a>ExecStartPost=-/opt/eset/eea/lib/install_scripts/launch_gui_all_users.sh</code></pre>



<p>The <code>ExecStartPost</code> script is starting the crashing egui</p>



<pre class="wp-block-code"><code><a href="#cb20-1"></a>root@L5:~# grep egui /opt/eset/eea/lib/install_scripts/*.sh
<a href="#cb20-2"></a>/opt/eset/eea/lib/install_scripts/egui_autorestart.sh:    /opt/eset/eea/lib/egui "$@" &amp;</code></pre>



<p>Why is that not inherting the systemd Environment settings? The <em>egui_autorestart.sh</em> is called from a script <em>launch_gui_all_users.sh</em>, so it probably launches after login. Could it be that the uses <code>su</code> for switching users, but forgetting to pass the environment?</p>



<p>Nope, it just calls <code>/opt/eset/eea/lib/egui "$@" &amp;</code></p>



<p>Hmm, who is actually starting <em>egui_autorestart.sh</em> then? Taking a closer look at the <code>ps</code> output, reveal it parent as pid 3845</p>



<pre class="wp-block-code"><code><a href="#cb21-1"></a>root@L5:~# ps -eo pid,comm,cmd | grep 3845
<a href="#cb21-2"></a>   3845 systemd         /usr/lib/systemd/systemd --user</code></pre>



<p>Enlightening. So the gui is started by the <em>systemd user instance</em>. I would have expected the system user instance to inherited the environment variables set in the service file, but that might not be the case?</p>



<pre class="wp-block-code"><code><a href="#cb22-1"></a>❱ systemctl --user show-environment | grep -E '(LC_|LANG)'
<a href="#cb22-2"></a>LANG=en_GB.UTF-8
<a href="#cb22-3"></a>LANGUAGE=en_GB
<a href="#cb22-4"></a>LC_ADDRESS=en_DK.UTF-8
<a href="#cb22-5"></a>LC_IDENTIFICATION=en_DK.UTF-8
<a href="#cb22-6"></a>LC_MEASUREMENT=en_DK.UTF-8
<a href="#cb22-7"></a>LC_MONETARY=en_DK.UTF-8
<a href="#cb22-8"></a>LC_NAME=en_DK.UTF-8
<a href="#cb22-9"></a>LC_PAPER=da_DK.UTF-8
<a href="#cb22-10"></a>LC_TELEPHONE=en_DK.UTF-8
<a href="#cb22-11"></a>LC_TIME=en_SE.UTF-8
<a href="#cb22-12"></a>LC_COLLATE=C.UTF-8
<a href="#cb22-13"></a>LC_CTYPE=C.UTF-8
<a href="#cb22-14"></a>LC_MESSAGES=C.UTF-8
<a href="#cb22-15"></a>LC_NUMERIC=en_US.UTF-8</code></pre>



<p>Strange output. Some values look fine, but some are missing and others have different values. Like, why do the environment have an seemingly arbitrary swedish time setting (<code>LC_TIME=en_SE.UTF-8</code>)?</p>



<p>My system setting has danish time setting</p>



<pre class="wp-block-code"><code>❱ localectl | grep LC_TIME
<a href="#cb23-2"></a>LC_TIME=en_DK.utf8</code></pre>



<p>Appears this is because the <code>systemd --user</code> session do not inherit all environment variables (ref section <a href="https://wiki.archlinux.org/title/Systemd/User">2.1</a> in the arch documentation). A bit strange though, that only <code>LC_TIME</code> has an apparently arbitrary setting.</p>



<p>The same Arch page suggests that <em>environment.d</em> can be used for fixing up environment variables like e.g.&nbsp;<code>LC_TIME</code>. <em>environment.d</em> is a systemd feature that lets you define persistent environment variables in simple files.</p>



<p>It reads files from:</p>



<ul class="wp-block-list">
<li><em>/etc/environment.d/</em></li>



<li><em>/usr/lib/environment.d/</em></li>



<li><em>~/.config/environment.d/</em></li>
</ul>



<p>I didn’t like this for several reasons:</p>



<ul class="wp-block-list">
<li>It’s quite hidden that an environment variable is overridden here</li>



<li>Its on system or user level, while the problem is in a specific program</li>
</ul>



<p>I decided instead to do a <a href="https://en.wikipedia.org/wiki/Laziness">KISS</a> solution and just patch the ESET script</p>



<p><em>/opt/eset/eea/lib/install_scripts/egui_autorestart.sh</em></p>



<pre class="wp-block-code"><code><a href="#cb24-1"></a>+   export LANG=C.UTF-8
<a href="#cb24-2"></a>+   export LC_ALL=C.UTF-8
<a href="#cb24-3"></a>    /opt/eset/eea/lib/egui "$@" &amp;</code></pre>



<p>Then restart</p>



<pre class="wp-block-code"><code><a href="#cb25-1"></a>root@L5:~# systemctl daemon-reload
<a href="#cb25-2"></a>root@L5:~# systemctl restart eea.service</code></pre>



<pre class="wp-block-code"><code><a href="#cb26-1"></a>root@L5:~# tail -f /var/log/syslog
<a href="#cb26-2"></a>
<a href="#cb26-3"></a>2025-04-22T09:51:42.836121+02:00 L5 systemd&#91;1]: eea_upgrade.service - ESET Endpoint Antivirus upgrade was skipped because of an unmet condition check (ConditionPathExists=/var/opt/eset/eea/updated/app/eea.bin).
<a href="#cb26-4"></a>2025-04-22T09:51:42.849737+02:00 L5 kernel: ESET Web access protection module 1.0 &lt;www.eset.com&gt; loaded
<a href="#cb26-5"></a>2025-04-22T09:51:43.243928+02:00 L5 systemd&#91;3845]: Reloading requested from client PID 42298 ('systemctl')...
<a href="#cb26-6"></a>2025-04-22T09:51:43.244022+02:00 L5 systemd&#91;3845]: Reloading...
<a href="#cb26-7"></a>2025-04-22T09:51:43.248730+02:00 L5 kernel: eset_rtp: registered syscall handlers
<a href="#cb26-8"></a>2025-04-22T09:51:43.248740+02:00 L5 kernel: ESET Real-time file system protection module 3.0 &lt;www.eset.com&gt;
<a href="#cb26-9"></a>2025-04-22T09:51:43.248742+02:00 L5 kernel: eset_rtp: scanner enabled
<a href="#cb26-10"></a>2025-04-22T09:51:43.467181+02:00 L5 systemd&#91;3845]: Reloading finished in 223 ms.
<a href="#cb26-11"></a>2025-04-22T09:51:43.506111+02:00 L5 systemd&#91;3845]: Starting eea-user-agent.service - ESET Endpoint Antivirus user monitoring service...
<a href="#cb26-12"></a>2025-04-22T09:51:43.945661+02:00 L5 systemd&#91;3845]: Finished eea-user-agent.service - ESET Endpoint Antivirus user monitoring service.
<a href="#cb26-13"></a>2025-04-22T09:52:00.934259+02:00 L5 egui&#91;43212]: Locale not supported by C library.#012#011Using the fallback 'C' locale.
<a href="#cb26-14"></a>2025-04-22T09:52:01.954114+02:00 L5 cron&#91;1719]: (*system*eset-eea-vapm) RELOAD (/etc/cron.d/eset-eea-vapm)</code></pre>



<p>And then suddenly the ESET gui popped up <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<figure class="wp-block-image size-full"><a href="https://monzool.net/blog/wp-content/uploads/2025/04/eset-gui.png"><img decoding="async" width="706" height="563" src="https://monzool.net/blog/wp-content/uploads/2025/04/eset-gui.png" alt="" class="wp-image-901" srcset="https://monzool.net/blog/wp-content/uploads/2025/04/eset-gui.png 706w, https://monzool.net/blog/wp-content/uploads/2025/04/eset-gui-300x239.png 300w" sizes="(max-width: 706px) 100vw, 706px" /></a></figure>
<p>The post <a href="https://monzool.net/blog/2025/04/24/eset-crashing-with-locale-error/">ESET crashing with locale error</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://monzool.net/blog/2025/04/24/eset-crashing-with-locale-error/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>My Favorite Dystopian Movies as of 2024</title>
		<link>https://monzool.net/blog/2024/11/07/my-favorite-dystopian-movies-as-of-2024/</link>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Thu, 07 Nov 2024 12:48:54 +0000</pubDate>
				<category><![CDATA[Entertainment]]></category>
		<category><![CDATA[Movies]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=891</guid>

					<description><![CDATA[<p>DYSTOPIAN MOVIES ARE a favorite genre of mine. In 2007 I made a list of my ten favorite movies in the category, and after revisiting that blog post, I thought now would be a good time to revise the list With no further ado, this is my new list: 28 [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2024/11/07/my-favorite-dystopian-movies-as-of-2024/">My Favorite Dystopian Movies as of 2024</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>DYSTOPIAN MOVIES ARE</strong> a favorite genre of mine. In 2007 I made a <a href="https://monzool.net/blog/2007/09/29/dystopian-movies/">list</a> of my ten favorite movies in the category, and after revisiting that blog post, I thought now would be a good time to revise the list</p>
<p>With no further ado, this is my new list:</p>
<ul>
<li><ol start="20" type="1">
<li><a href="https://www.imdb.com/title/tt0289043">28 Days Later (2002)</a></li>
</ol></li>
<li><ol start="19" type="1">
<li><a href="https://www.imdb.com/title/tt0238380">Equilibrium (2002)</a></li>
</ol></li>
<li><ol start="18" type="1">
<li><a href="https://www.imdb.com/title/tt0112682">The City of Lost Children (1998)</a></li>
</ol></li>
<li><ol start="17" type="1">
<li><a href="https://www.imdb.com/title/tt0134847">Pitch Black (2000)</a></li>
</ol></li>
<li><ol start="16" type="1">
<li><a href="https://www.imdb.com/title/tt1823672">Chappie (2015)</a></li>
</ol></li>
<li><ol start="15" type="1">
<li><a href="http://www.imdb.com/title/tt0088846/">Brazil (1985)</a></li>
</ol></li>
<li><ol start="14" type="1">
<li><a href="https://www.imdb.com/title/tt0206634">Children of Men (2006)</a></li>
</ol></li>
<li><ol start="13" type="1">
<li><a href="#%20V%20for%20Vendetta">V for Vendetta (2005)</a></li>
</ol></li>
<li><ol start="12" type="1">
<li><a href="https://www.imdb.com/title/tt1535108/">Elysium (2013)</a></li>
</ol></li>
<li><ol start="11" type="1">
<li><a href="http://www.imdb.com/title/tt0093870/">RoboCop (1987)</a></li>
</ol></li>
<li><ol start="10" type="1">
<li><a href="https://www.imdb.com/title/tt3315342">Logan (2017)</a></li>
</ol></li>
<li><ol start="9" type="1">
<li><a href="http://www.imdb.com/title/tt0094625/">Akira (1988)</a></li>
</ol></li>
<li><ol start="8" type="1">
<li><a href="http://www.imdb.com/title/tt0119177/">Gattaca (1997)</a></li>
</ol></li>
<li><ol start="7" type="1">
<li><a href="https://www.imdb.com/title/tt1136608">District 9 (2009)</a></li>
</ol></li>
<li><ol start="6" type="1">
<li><a href="https://www.imdb.com/title/tt11858890/">The Creator (2023)</a></li>
</ol></li>
<li><ol start="5" type="1">
<li><a href="https://www.imdb.com/title/tt0088247">The Terminator (1984)</a></li>
</ol></li>
<li><ol start="4" type="1">
<li><a href="http://www.imdb.com/title/tt0118929/">Dark City (1998)</a></li>
</ol></li>
<li><ol start="3" type="1">
<li><a href="http://www.imdb.com/title/tt0083658/">Blade Runner (1982)</a></li>
</ol></li>
<li><ol start="2" type="1">
<li><a href="https://www.imdb.com/title/tt1343727">Dredd (2012)</a></li>
</ol></li>
<li><ol type="1">
<li><a href="http://www.imdb.com/title/tt0078748/">Alien (1979)</a></li>
</ol></li>
</ul>
<p>The Matrix went out. I think I just saw that movie too many times <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f383.png" alt="🎃" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Otherwise the majority of the top of the list is probably unsurprising for most people (including myself).</p>
<p>Movies like <em>Alien</em> and <em>Blade Runner</em> are just timeless classics that is as good today, as they were then. Same goes for classics like <em>The Terminator</em> and <em>RoboCop</em> &#8211; although they perhaps show there age a bit more.</p>
<p>I placed <em>Dredd</em> at second best. I just absolutely love that movie. Its brutal, claustrophobic, have an bad-ass anti-hero and is absolutely dystopian.</p>
<p>It is obvious that most of the movies are rather old by now. I made the original list in 2007, and only six movies created since the first list, made it to the list! But its a list of all good movies, and it makes me want to see them all again. It’s been a really really long time since I saw <em>Dark City</em>, <em>Gattaca</em> and <em>Brazil</em>. I think I’ll start there &#8211; they are perfect for a dark and rainy winter night <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4fd.png" alt="📽" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>

<p>The post <a href="https://monzool.net/blog/2024/11/07/my-favorite-dystopian-movies-as-of-2024/">My Favorite Dystopian Movies as of 2024</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Grokking Simplicity</title>
		<link>https://monzool.net/blog/2024/08/15/grokking-simplicity/</link>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Thu, 15 Aug 2024 05:59:05 +0000</pubDate>
				<category><![CDATA[Books (technical)]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Reviews]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=873</guid>

					<description><![CDATA[<p>GROKKING SIMPLICITY IS a book that shows how to use functional design principals to reduce complexities in software. The books examples are in javascript, but should apply well to C and similar languages also. Abstract Chapters: Actions, calculations and data First two chapters are about learning some basic design principals. [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2024/08/15/grokking-simplicity/">Grokking Simplicity</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>GROKKING SIMPLICITY IS</strong> a book that shows how to use functional design principals to reduce complexities in software. The books examples are in javascript, but should apply well to C and similar languages also.</p>
<p><img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/08/Grokking_Simplicity_cover.png" /></p>
<h2 id="abstract">Abstract</h2>
<h3 id="chapters-actions-calculations-and-data">Chapters: Actions, calculations and data</h3>
<p>First two chapters are about learning some basic design principals. Three fundamental categories are identified:</p>
<ul>
<li>Actions</li>
<li>Calculations</li>
<li>Data</li>
</ul>
<p>In perhaps more common functional terms, <em>actions</em> are <em>impure</em> functions and <em>calculations</em> are <em>pure</em> functions. The reader is introduced to these concept gradually, by a guided tour of refactoring a (obviously worst-case) piece of imperative software. While this is done in iterations, the pros and cons are discussed.</p>
<p>The lesson is to learn how to eliminate implicit inputs and outputs, and how to convert actions into calculations</p>
<h3 id="chapter-improving-designs">Chapter: Improving designs</h3>
<p>Some fundamentals about the problems of global variables are discussed, and the reader is then guided through how the example code can be changed to avoid these.</p>
<p>The example in the book is a shopping cart being added with items and a total cost is calculated. The exercise of this chapter is to identify which functions have to many concerns and operate on both the cart and items and even also business logic. The agenda is to separate concerns and then discuss the implications of these refactorings.</p>
<p>Also discussed are how functions that do general things, should not have domain specific namings. The concrete example is a function to append an item to the cart, which essentially is making a copy of the array and returning it with the new item added last. That is refactored like this:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="st">-function add_item(cart, item) {</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="va">+function add_element_last(array, elem) {</span></span></code></pre></div>
<p>The point of this chapter is to realize, that only dedicated functions should know the integrate details of the data structures used. These function should then expose general functions. The rest of the code should then only have an opaque knowledge of the data, and instead use the general functions to manipulate it. At the end of the chapter, functions that did actions, calculations and data changing, now only do only of the things.</p>
<h3 id="chapter-immutable-in-a-mutable-language">Chapter: Immutable in a mutable language</h3>
<p>Immutable data has already been introduced, but in this chapter it goes full fletched on immutable data. Functions are identified by their read and/or access to data. Next it is explained how <em>copy-on-write</em> can be used to convert write functions to read functions.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>function delete_handler(name) {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="st">-   remove_item_by_name(shopping_cart, name);</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="va">+   shopping_cart = remove_item_by_name(shopping_cart, name);</span></span></code></pre></div>
<p>As more and more functions become immutable, it is shown how the code can be optimized to only make copies when necessary</p>
<p>The example code in the book is in javascript, and is thus a language with mutable data structures. In javascript <code>Array.shift</code> is both a read and write function. An example is made to show how one can create immutable alternatives to the <code>shift</code> functionality.</p>
<h3 id="chapter-handle-untrusted-code">Chapter: Handle untrusted code</h3>
<p>An immutable codebase that has to interact with an mutable codebase, comes with a lot of potential issues. This chapter shows how to use <em>defensive copying</em> as a method to defend the immutable strategy. The chapter also discusses <em>shallow copying</em> versus <em>deep copying</em>.</p>
<h3 id="chapters-stratified-design">Chapters: Stratified design</h3>
<p>Stratified design is a technique for building software in layers. The book list four design patterns for a stratified design.</p>
<p>This first pattern is “Straightforward implementations” which is about splitting code up for higher cohesion and placing the extracted parts in relevant layers. There is an interesting technique used, where the internals of a function is visualized as a call-graph:</p>
<p><img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/08/stratified_design_breakdown.png" /></p>
<p>By evaluating the levels of abstraction in the call-graph, it is possible to see what to split out, as <strong>straightforward implementations should call functions from similar layers of abstraction</strong>. With this principal rule, the reader is guided though how to identify how functions access layers, and how to mitigate if the rule is not followed.</p>
<p>The end result is more general functions that allows more code reuse, and a call-graph that reduces “spaghetti code”.</p>
<p>The second pattern is “Abstraction barriers”. This is about creating an api of sorts, where the upper functions do not depend on implementation details of the lower layer. Exposing only opaque data types and general functions help to accomplish this. The concepts is especially useful for delivering functionality to a second development team.</p>
<p>The third, and penultimate, pattern is “Minimal interface”. The agenda here is to provide an <em>abstraction barrier</em> that is sufficient flexible for the layer above the barrier to add additional features from the building block provided by the abstraction.</p>
<p>Last pattern is “Comfortable layers”. This is a reminder that we also need to be practical. In production code the business need will often outweigh the timely efforts of making the perfectly stratified solution. Also highlighted in the chapter is the fact that lower levels carry higher risk. There are some observation about how to three functions in different layers. From that, three nonfunctional requirements rules are identified: Maintainability, Testability and Reusability.</p>
<h3 id="chapters-first-class">Chapters: First-class</h3>
<p>Here is explained how enumerations and functions can be used as arguments to reduce code duplication, and improve extensibility of functionality without causing great impacts.</p>
<p>It is also shown how <em>higher-order functions</em> can compose new functionality with great flexibility. The example is how to add a logging system to existing code, without having to plaster logging calls all over the code base.</p>
<h3 id="chapters-functional-tools">Chapters: Functional tools</h3>
<p>Functional tools like <code>for-each</code>, <code>map</code>, <code>filter</code> and <code>reduce</code> are explained along with the benefits they can bring to code. They are very well explained from an imperative standpoint.</p>
<p>Concepts as chaining functions are then covered before continuing on to <code>update</code>. When handling nested data, then instead of manually using <code>get</code> + <code>modify</code> + <code>set</code> an approach of using an update function is proposed.</p>
<p><img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/08/replace_get_modify_set_with_update.png" /></p>
<p>An update function is created that eventually lead into explaining recursion. Also this is explained really well with clear explanations and informative illustrations.</p>
<h3 id="chapters-timelines">Chapters: Timelines</h3>
<p>The example code in this book, is a web client that put items in a cart. As the client has to communicate with the server for price calculation, it shows a plausible scenario where the timing can go wrong and incorrect totals are presented to customers. For visualizing this, this chapter teaches the reader how to visualize timings with <em>timeline diagrams</em></p>
<p><img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/08/timeline_diagram_example.png" /></p>
<p>The chapter also have some good insights on what can go wrong in different models like async, single-threading sync/async and multi-threading. The browsers <em>job queue</em> is also discussed along with ajax requests. A lot of good details are touched in these chapters.</p>
<h3 id="chapter-reactive-vs-onion-design">Chapter: Reactive vs onion design</h3>
<p>This chapter starts out by showing how to implement <em>reactive</em> updating (like React etc.). It has a nice story about the pros and cons to such a design and what kind of changes to the system design the reactive strategy entails in regards to decoupling and timelines.</p>
<p>The other design pattern <em>onion</em> is then discussed. The onion design leans into functional programming and strive to separate side-effects from pure functionality.</p>
<p><img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/08/onion_design.png" /></p>
<p>The signature idea about the onion design align well with the actions/calculation concepts discussed throughout the book:</p>
<ul>
<li>Interaction to external entities, is done <strong>only</strong> in the interaction layer.</li>
<li>Layers call in toward the center.</li>
<li>Layers don’t know about layers outside of themselves.</li>
</ul>
<p>There are some great points to how common MVC design patters are not good for functional desires to isolate impure functions. This is what onion design tries to do &#8211; keep a clean separation between calculations and actions</p>
<h2 id="evaluation">Evaluation</h2>
<p>This book is perfect for any typical imperative programmer that have heard all the hype of functional programming merits, but never really grokked how to begin doing actual functional programming. The book gently transforms a typical (albeit worst-case) imperative application to incorporate more and more functional programming functions and design patterns. It does this with clear, and to the point examples. To improve understanding, a lot of really well made informative diagrams, drawings are supplied. Perhaps the best trait of this book, are the many users-stories (from a developer viewpoint) that all helps to understand <strong>why</strong> the proposed changes improves the overall design. The target group for this book is definitely beginner programmers, or people that have no theoretical or practical experience with functional programming concepts; but <strong>if</strong> this fits your bill, you will for sure learn a bunch from this book.</p>
<p><br/></p>
<p>Rating: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>
<p>The post <a href="https://monzool.net/blog/2024/08/15/grokking-simplicity/">Grokking Simplicity</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>A Second Search for Bash Scripting Alternatives</title>
		<link>https://monzool.net/blog/2024/07/14/a-second-search-for-bash-scripting-alternatives/</link>
					<comments>https://monzool.net/blog/2024/07/14/a-second-search-for-bash-scripting-alternatives/#comments</comments>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Sun, 14 Jul 2024 12:15:33 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Shell Scripting]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=833</guid>

					<description><![CDATA[<p>A second search for bash scripting alternatives SHELL SCRIPTING IS something most programmers will encounter often &#8211; especially if doing dev-ops, automation and general Linux work. Unfortunately we are mostly stuck with bash. There surely must be better alternatives… I have been in search for a bash replacement before. My [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2024/07/14/a-second-search-for-bash-scripting-alternatives/">A Second Search for Bash Scripting Alternatives</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h1 style="color:DodgerBlue;">
A second search for bash scripting alternatives
</h1>
<p><strong>SHELL SCRIPTING IS</strong> something most programmers will encounter often &#8211; especially if doing dev-ops, automation and general Linux work. Unfortunately we are mostly stuck with bash. There surely must be better alternatives…</p>
<p>I have been in search for a bash replacement <a href="https://monzool.net/blog/2017/07/04/a-search-for-bash-scripting-alternatives/#finalists">before</a>. My conclusions was that I would try out: <a href="https://scsh.net/">scsh</a>, <a href="https://github.com/zserge/luash">luash</a> and <a href="https://mruby.org/">mruby</a>. I’m addressing that experience in Addendum <a href="#addendum-take-1-anchor">Take 1</a>,</p>
<p>Since my last endeavor into finding a bash alternative, I’ve seen my work-life distance myself more and more from the small embedded devices, and move into higher level domains of JavaScript, Python and C#. In the environments where these run, the restrictions that apply to small embedded devices are not really applicable, and the options for scripting is much more open. Now I find myself back in the embedded world, and thus I figured it was time for another round of looking at bash alternatives. A lot of interesting stuff has happened in the programming language world these last years. That also include the scripting languages, where there seem to be a consensus that we might deserve more modern alternatives.</p>
<p>This post is available in two places</p>
<ul>
<li><p>On the monzool.net <a href="https://monzool.net/blog/2024/07/14/a-second-search-for-bash-scripting-alternatives/">blog</a>, for a pretty viewing.</p></li>
<li><p>On <a href="https://github.com/monzool/A-Search-for-BASH-Scripting-Alternatives">github</a> where the source code is also available. This post is the <em>Take2</em> part of this series (direct preview link here: <a href="https://github.com/monzool/A-Search-for-BASH-Scripting-Alternatives/blob/master/Take2/Take2.md">preview</a>)</p></li>
</ul>
<hr />
<a name="approach-anchor"></a>
<h1 style="color:DodgerBlue;">
The approach
</h1>
<p>In this post I will not sprinkle code examples to exemplify how each and individual language look, feel and have of specialties. Nor will I do a deep dive into each of them. I therefore encourage visiting each individual homepage of each language to make you own opinion.</p>
<p>What I <strong>will</strong> do, is to actually try out the language, instead of just do assumptions about them. For each language I will implement the same sample, that do the following:</p>
<ol type="1">
<li><strong>Change current directory to that of the script</strong>. With bash scripts I sometimes change the current directory, to be the directory of the script. It depends, if this improves the user experience, or makes it worse. When the user have to provide paths as program arguments, is a bad idea. If the script requires to know the location of a lot of external files, is makes thing easier…</li>
<li><strong>Get program arguments from command-line</strong>. I rarely do fancy stuff with options, but is a feature I use often. Basic support is fine</li>
<li><strong>Find files and get a list of found files</strong>. Searching for files is paramount &#8211; but resorting to plain old <code>find</code> is perfectly fine, if the output is easy to grab.</li>
<li><strong>Split path, file name and file extension</strong>. This requires string manipulation.</li>
<li><strong>Group files by extension</strong>. This is best solved with dictionaries.</li>
<li><strong>Get a random number</strong>. Not an essential feature (the operating system I use, have <em>/dev/urandom</em>)</li>
<li><strong>Search a file for some text string</strong>. This is probably my bread on butter use-case. It would actually benefit if this could be a call to <code>grep</code> (or <code>rg</code>) as these tools work perfectly for what they do.</li>
<li><strong>Pretty print in colors</strong>. I am a heavy user of color printing. Its an easy way to bring focus on the essential parts.</li>
</ol>
<p>A little introduction to this exercise. I first made a small sample script in bash. It takes to optional arguments. One that points to a directory of files, and another argument to possible disable color output</p>
<p>This is how the output looks in color</p>
<figure>
<img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/07/sample_bash.png" alt="bash sample output" />
<figcaption aria-hidden="true">bash sample output</figcaption>
</figure>
<p>And with color suppression</p>
<figure>
<img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/07/sample_bash_no_color.png" alt="bash sample output" />
<figcaption aria-hidden="true">bash sample output</figcaption>
</figure>
<p>The exercise is then to implement the equivalent in the other scripting languages, while <strong>learning the language is I go</strong>. I will be writing my thoughts and findings during the exercise, <strong>including any doubts, misunderstandings and stupid mistakes</strong>. I’m sure many will laugh of my struggles, but I think it will be a good exercise to get some indication on the difficulties learning the language for the average programmer. The eventual struggles I had, would probably also be the struggles of my colleagues. The friction of introducing a new language should be as little as possible.</p>
<p>Disclaimer: I will critique things here that someone or someones put their heart and soul into. This surely entail a risk that I unintentionally offend your favorite language, or even worse, a language that <strong>you</strong> created. Please be assured that I am not criticizing you personally or your work directly. In fact I am most likely just another idiot on the internet, ranting about stuff that I do not understand <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" />. I for sure do not own the abilities to create such exiting stuff, so I salute you for your efforts and work <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f64f.png" alt="🙏" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<hr />
<a name="languages-anchor"></a>
<h1 style="color:DodgerBlue;">
Languages
</h1>
<p>I am searching for a scripting language, that</p>
<ul>
<li>Have proper arrays and dictionaries</li>
<li>Have versatile return values</li>
<li>Do not have “everything is a string” syndrome</li>
<li>Can call external programs and grab values and exit code from it</li>
<li>Is low on cruft and boilerplate, but still readable</li>
<li>Do not use dynamic scoping</li>
<li>Is production ready</li>
<li>Is not abandonware</li>
<li>Is an actual interpreted language, where scripts can be edited on a remote target</li>
</ul>
<p>Some of the languages also provide a shell. For the intended purpose, this is not important and bares no positive or negative impact, as long as the language can be used with another shell.</p>
<p>Documentation and examples weight in hard as a requirement, as otherwise guessing and confusion would entail and make the experience frustrating and unproductive &#8211; both for the writer and reviewer/reader.</p>
<p>Below is a list of languages in the pool for trying out. Each is marked with the following icons to indicate the level of application</p>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /> &#8211; have an implementation of the sample code<br />
<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4eb.png" alt="📫" class="wp-smiley" style="height: 1em; max-height: 1em;" /> &#8211; attempted sample implementation<br />
<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ea.png" alt="📪" class="wp-smiley" style="height: 1em; max-height: 1em;" /> &#8211; no sample consdered<br />
<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5d1.png" alt="🗑" class="wp-smiley" style="height: 1em; max-height: 1em;" /> &#8211; not considered</p>
<h2 style="color:MediumVioletRed;">
Shells
</h2>
<ul>
<li><a href="#bash-anchor">bash</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#elvish-anchor">elvish</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4eb.png" alt="📫" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#murex-anchor">murex</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4eb.png" alt="📫" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#ion-anchor">ion</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4eb.png" alt="📫" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#nushell-anchor">nushell</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
</ul>
<h2 style="color:MediumVioletRed;">
Shell scripting languages
</h2>
<ul>
<li><a href="#hush-anchor">hush</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#koi-anchor">koi</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#abs-anchor">abs</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#ysh-anchor">ysh</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
</ul>
<h2 style="color:MediumVioletRed;">
Clojures
</h2>
<ul>
<li><a href="#babashka-anchor">Babashka</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ea.png" alt="📪" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#planck-anchor">Planck</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ea.png" alt="📪" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#joker-anchor">Joker</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ea.png" alt="📪" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
</ul>
<h2 style="color:MediumVioletRed;">
Lisps
</h2>
<ul>
<li><a href="#fennel-anchor">Fennel</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ea.png" alt="📪" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li><a href="#janet-anchor">Janet</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4ec.png" alt="📬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
</ul>
<h2 style="color:MediumVioletRed;">
Rejected contenders
</h2>
<p>There are some really interesting languages out there that unfortunately do not fit within all parameters, regarding being applicable for a small embedded system.</p>
<ul>
<li><p><a href="https://elk.strct.net/">elk</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5d1.png" alt="🗑" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>This is a tremendously interesting project. It looks very complete, versatile and pretty much check-marking all the <a href="https://elk.strct.net/other/bash-literals.html">boxes</a> . There is just that issue that its a .NET 8 application and requires several hundreds of megabyte in memory and storage. There are some efforts in progress getting NativeAOT to cross-compile to <a href="https://github.com/dotnet/runtime/issues/97729">arm32</a>, but for now I do not consider it a viable contender</p></li>
<li><p><a href="https://dart.dev/">dart</a> with <a href="https://dcli.onepub.dev/">dlic</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5d1.png" alt="🗑" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>dlic looks very interesting, and dart has a rumour of being very approachable. There is unfortunately a relatively high size and memory use if to be used as non-compiled scripts. <a href="https://www.nequalsonelifestyle.com/2021/12/13/dart-minimum-file-mem-and-run-time/">Hank G</a> have some interesting measurements indicating up to 100 MB of memory usage for the runtime. If abandoning script interpretation and settle for complied programs, the resource usage can approach about a tenth of that.</p></li>
<li><p><a href="https://gleam.run/">gleam</a> or <a href="https://elixir-lang.org/">elixir</a> on <a href="https://www.atomvm.net">AtomVM</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5d1.png" alt="🗑" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>I’ve been making a few small projects in gleam. Its a interesting language and a very well driven project overall. The BEAM ecosystem is probably a bit on the bigger side. There is the AtomVM alternative, but it is unclear if gleam or elixir is able to run on that. Both are compiled languages, so they don’t really fit the agenda anyway.<br />
(P.S. consider doing like me, and <a href="https://github.com/sponsors/lpil">sponsor</a> the amazing gleam project).</p></li>
<li><p><a href="https://github.com/sekiguchi-nagisa/arsh">arsh</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f5d1.png" alt="🗑" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><a href="https://github.com/sekiguchi-nagisa/arsh/blob/master/sample/todo.ds">Appears</a> to be bash with types? The documentation is sadly pretty much non-existing.</p></li>
</ul>
<h2 style="color:MediumVioletRed;">
Future contenders
</h2>
<p>For sure I have not depleted the realm of bash alternatives, so I am going to survey additional languages, either in this post or another. The addendum <a href="addendum-future-candidates">Future candidates</a> has more details.</p>
<hr />
<p><br/> <br/></p>
<a name="bash-anchor"></a>
<h1 style="color:DodgerBlue;">
bash
</h1>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<p>The sample task is obvious to solve with a dictionary, where the key is the extension, and the values are a list of files that have that extension. Bash do have associative arrays, but do not support arrays of arrays. This puts on some limitations on how to construct the solution.</p>
<p>For each key (file extension) I serialize the list of files associated to that extension.</p>
<pre class="sh"><code>file_list[&quot;${extension}&quot;]+=$&#39;\n&#39;&quot;${filename}&quot;</code></pre>
<p>When printing, I then deserialize to get the filenames back in entity form.</p>
<pre class="sh"><code>deserialize_array() {
    local serialized=&quot;${1}&quot;
    IFS=$&#39;\n&#39; read -r -d &#39;&#39; -a file_category &lt;&lt;&lt; &quot;${serialized}&quot; || :
}</code></pre>
<p>The implemented solution actually have a limitation, in that it does not support filenames with newlines, as <code>\n</code> is used as serializing separator (I did attempt to use <code>\0</code> as that is not a valid character in filenames, but I could not get that to work).</p>
<p>For getting the file list, I’ve resorted to just use <code>find</code>. Now, bash could glob the files</p>
<pre class="sh"><code>for file in &quot;${dir}&quot;/**; do
    if [[ ! -f &quot;${file}&quot; ]]; then
        continue
    fi
    file_list+=( &quot;${file}&quot; )
done</code></pre>
<p>but for this to work, then you need to remember to poke the shell options <code>shopt -p globstar</code> &#8211; and possibly remember to restore them again…. it gets complicated.</p>
<p>The sharp observer will notice that the <code>IFS</code> is temporarily poked for find files.</p>
<pre class="sh"><code>while IFS= read -r -d &#39;&#39; file; do
    file_list_all+=(&quot;${file}&quot;)
done &lt; &lt;(find &quot;${dir}&quot; -type f -print0)</code></pre>
<p>Its a common technique prevent word splitting and pathname expansion… <code>IFS</code> is complicated</p>
<h2 style="color:MediumVioletRed;">
Parsing program arguments
</h2>
<p>Bash have <a href="https://man7.org/linux/man-pages/man1/getopts.1p.html"><code>getopts</code></a> builtin for parsing program argument. There is also <a href="https://www.man7.org/linux/man-pages/man1/getopt.1.html">getpopt</a> for some additional features. I rarely use ’em. They both kinda suck, are both complicated and I can never remember how they work. A simple loop of case matching, is far sufficient for most of my situations</p>
<pre class="sh"><code>while [[ $# -gt 0 ]]; do
    case &quot;$1&quot; in
        --list-dir)
            option_list_dir=&quot;${2}&quot;
            shift 2
            ;;</code></pre>
<h2 style="color:MediumVioletRed;">
Passing arguments to functions
</h2>
<p>Passing arguments to bash functions are kind of old school. There are no parameter list, instead arguments are passed in to be referenced by a numeric index variable. First argument is <code>${1}</code>, second argument is <code>${2}</code> and so on. I’m used to it, but its kind of odd, come to think of it. There are various gotcha’s when having values with spaces, but otherwise it works as expected… Except for associative arrays &#8211; then it gets complicated. Therefor I resorted to pass them as references</p>
<pre class="sh"><code>present_results() {
    declare -n extension_count_ref=&quot;${2}&quot;
    declare -n file_list_ref=&quot;${3}&quot;</code></pre>
<p>It might come as a surprise that bash supports this kind of indirection, but bash is fully dynamic and quite powerful in that domain; but yeah, it gets complicated pretty fast. The terse syntax also does not make it easier to read later on <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
Returning values from functions
</h2>
<p>Why even mention such a trivial matter as returning values from? Well in bash its… limited. We either return integer values or echo strings. Using <code>echo</code> to return strings, makes other printing complicated which, among other things, can make debugging hard. The basic string return, do let you set your own rules when in a script. It is for example possible to fake tuple like return values</p>
<pre class="sh"><code>get_tuple() {
    echo &quot;Year|2024&quot;
}

IFS=&#39;|&#39; read -r message value &lt;&lt;&lt; &quot;$(get_tuple)&quot;</code></pre>
<p>But using such tricks, quite quickly make things get… complicated. Being bash there’s lots of hacks to return other ways, but most have some footgun or another… it gets complicated.</p>
<p>I do use the above mentioned trick sometimes, but over time I’ve mostly settled on a simpler principle: return zero or non-zero if to indicate success or fail, and if returning a value, put it in a global variable. This might sound chocking, but in practice it works quite fine.</p>
<pre class="sh"><code>return_value=
get_return_value() {
    return_value=&quot;magic&quot;
}</code></pre>
<p>It do have its limitation in some corner cases (its a global variable after all), but its not much worse that bash’s dynamic scoping anyway. What it certainly does is reducing complexity instead of loops and tricks and hacks to simulate proper return values. One key principal to limit the “pollution” of global variables, is then to never use the variable directly, except as arguments to other functions</p>
<pre class="sh"><code>use_return_value() {
    local value=&quot;${1}&quot;
    echo &quot;${value}&quot;
}

use_return_value &quot;${return_value}&quot;</code></pre>
<p>Theres a “How I Learned to <em>Stop</em> Worrying and Love the <em>global return value</em>” blog post material lingering there. I’ll probably never get there, so just keep calm and carry on <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1fae1.png" alt="🫡" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>On an interesting note, then its possible to simulate named parameters in bash. However, I can testify its a submachine gun of a footgun, so I never do that anymore <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f604.png" alt="😄" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Speaking of footguns…</p>
<h3 id="footguns">Footguns</h3>
<p>What does this snippet print?</p>
<pre class="sh"><code>get() {
    return false
}
get &amp;&amp; echo success || echo fail</code></pre>
<blockquote>
<p>return: false: numeric argument required</p>
</blockquote>
<p>Below is how it is actually supposed to be:</p>
<pre class="sh"><code>get() {
    false
}
get &amp;&amp; echo success || echo fail</code></pre>
<p>That’s just one footgun that I got hit by in the sample script.</p>
<p>In an early incarnation of the sample script, this was how the line to increment the extension count looked like</p>
<pre class="sh"><code>(( extension_count[&quot;${extension}&quot;]++ ))</code></pre>
<p>At first glance this seems fine, but in fact it is not. It can be very puzzling, as the script will exit on that line. No error, just exit.</p>
<p>Inspecting the exit code, gives some indication</p>
<pre class="sh"><code>❱ echo $?
1</code></pre>
<p>What happens is that lookup returns 0, then incrementing causes the overall expression to return non-zero, which then triggers <code>errexit</code> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f926-200d-2640-fe0f.png" alt="🤦‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Lets just bypass <code>errexit</code> and override the non-zero with a NOP</p>
<pre class="sh"><code>(( extension_count[&quot;${extension}&quot;]++ )) || :</code></pre>
<p>Similar issue is the reason why the deserializer looks like this</p>
<pre class="sh"><code>deserialize_array() {
    local serialized=&quot;${1}&quot;
    IFS=$&#39;\n&#39; read -r -d &#39;&#39; -a deserialized &lt;&lt;&lt; &quot;${serialized}&quot; || :
}</code></pre>
<p>In this configuration, <code>read</code> returns non-zero on EOF &#8211; which we will inevitable reach</p>
<p>The unofficial bash “safe” mode is not without its issues <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><br/> <br/></p>
<a name="elvish-anchor"></a>
<h1 style="color:DodgerBlue;">
elvish
</h1>
<p><a href="https://elv.sh">elvish</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>Elvish is an amazing effort that just continues to improve, commit after commit, year after year. Unfortunately I decided to cut the sample short before completing it. I would have loved to say the documentation was awesome, but I spent way too much time on every single thing that I wanted to do.</p>
<p>vscode plugin: https://marketplace.visualstudio.com/items?itemName=elves.elvish</p>
<p>Shell: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>
<h2 style="color:MediumVioletRed;">
Returning values from functions
</h2>
<p>I browsed the entire <a href="https://elv.sh/learn/">learn</a>section with no hint of how to return something from a function. I had to visit the reference to find an <a href="https://elv.sh/ref/language.html#function">example</a> that used <code>echo</code> to return a string.</p>
<p>In fact, the first thing that looks like a function was <a href="https://elv.sh/learn/scripting-case-studies.html#update-servers-in-parallel.elv">this</a> example:</p>
<pre><code>var hosts = [[&amp;name=a &amp;cmd=&#39;apt update&#39;]
             [&amp;name=b &amp;cmd=&#39;pacman -Syu&#39;]]
peach {|h| ssh root@$h[name] $h[cmd] } $hosts</code></pre>
<p>The explanation was clear enough, so rolled my own edition</p>
<pre class="elvish"><code>say {|message| put $message} &quot;Hello, world!&quot;</code></pre>
<p>But got this back</p>
<pre class="error"><code>Exception: exec: &quot;say&quot;: executable file not found in $PATH</code></pre>
<p>Oh. Not a function definition, but instead a command. Lots of reading later I was able to put this together</p>
<pre class="elvish"><code>fn say {|message| put $message }
say &quot;Hello, world!&quot;
&#x25b6; &#39;Hello, world!&#39;</code></pre>
<p>As mentioned, a frustrating lot of reading was then required to figure out, how to make a function return stuff. Eventually I tumbled upon a tiny example that used <code>put</code> to return a value. It might be that I had some preconceived ideas on things, but I didn’t expect elvish to rely on capturing printed stuff as return values.</p>
<p>Finding the <code>put</code> documentation was not easy, but eventually I found a mention <a href="https://elv.sh/learn/tour.html#value-output">here</a>.</p>
<table>
<colgroup>
<col style="width: 57%" />
<col style="width: 42%" />
</colgroup>
<thead>
<tr>
<th>Command</th>
<th>Functionality</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://elv.sh/ref/builtin.html#put"><code>put</code></a></td>
<td>Writes arguments as value outputs</td>
</tr>
</tbody>
</table>
<p>which then lead to the <a href="https://elv.sh/ref/builtin.html#put">actual documentation</a></p>
<p><code>put</code> &#8211; <em>Takes arbitrary arguments and write them to the structured stdout.</em></p>
<p>Interesting with the <em>structured stdout</em>, because <code>printf</code> outputs to something else</p>
<p><code>printf</code> &#8211; <em>Prints values to the byte stream according to a template.</em></p>
<p>So from deduction, I could reason that elvish in fact use <code>put</code> for returning values from functions, but have different “channels” of returning them <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>After quite some searching, I found an <a href="https://elv.sh/learn/effective-elvish.html#returning-values-with-structured-output">example</a> of how to capture the return values. It appears to work, and even for lists</p>
<pre class="elvish"><code>fn get-url {
    put [&quot;monzool&quot; &quot;.&quot; &quot;net&quot;]
}

var url = (get-url)
echo $url

&#x25b6; [monzool . net]</code></pre>
<p>That naturally made me curious. What if you have more that one <code>put</code> or <code>printf</code>?</p>
<pre class="elvish"><code>fn get-url {
    printf &quot;Debug: calling get-url&quot;
    put [&quot;monzool&quot; &quot;.&quot; &quot;net&quot;]
}

var url = (get-url)</code></pre>
<p>This gives a compile error:</p>
<pre class="error"><code>Exception: arity mismatch: assignment right-hand-side must be 1 value, but is 2 values</code></pre>
<p>Sweet <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>I then tested <code>echo</code>, <code>printf</code> and <code>put</code>. None print directly to stdout, but are all captured, with <strong>the compiler forcing a receiving variable for all</strong>.</p>
<p><em>sample.elv</em></p>
<pre class="elvish"><code>fn get-values {
    echo &quot;first&quot;
    printf &quot;second&quot;
    put &quot;third&quot;
}

var first second third = (get-values)
printf &quot;first = %s\n&quot; $first
printf &quot;second = %s\n&quot; $second
printf &quot;third = %s\n&quot; $third</code></pre>
<p>The above sample lead to much confusion. On first run I thought I had the order wrong.</p>
<pre class="sh"><code>❱ ./sample.elv
first = third
second = second
third = first</code></pre>
<p>Running it a few more times showed that, that was not the issue. Its arbitrary which variable receives which value from the function</p>
<pre class="sh"><code>❱ ./sample.elv
first = first
second = second
third = third</code></pre>
<pre class="sh"><code>❱ ./sample.elv
first = second
second = third
third = first</code></pre>
<p>First thought was that tings were running in parallel, but it’s actually is a documented <a href="https://elv.sh/ref/language.html#output-capture">“feature”</a></p>
<p><em>If the chunk outputs both values and bytes, the values of output capture will contain both value outputs and lines. However, the ordering between value output and byte output might not agree with the order in which they happened</em></p>
<p>Sure enough, switching to same print function, make the return values predictable</p>
<pre class="elvish"><code>fn get-values {
    put &quot;first&quot;
    put &quot;second&quot;
    put &quot;third&quot;
}</code></pre>
<p>Interesting footgun <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e8.png" alt="🧨" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>I sort of got fatigued here. At first glance, the documentation looks very elaborate, but I found it very very difficult to get started with just the basic things. I even (desperately) tried to find some inspiration in sample code or unit-tests in the elvish repo. The few things I found was primarily in the implementation language Go.</p>
<h2 style="color:MediumVioletRed;">
Error handling
</h2>
<p>The thing that turns me interest away from elvish, is its use of <a href="https://elv.sh/learn/tour.html#exceptions">exceptions</a></p>
<p><em>[snip] non-zero exits from external commands are also turned into exceptions:</em></p>
<p>IMO exceptions should be for the <em>exceptional</em> case. An external command returning non-zero is not an exceptional case &#8211; it might not even be an error situation</p>
<pre class="sh"><code>grep ERROR run_with_no_error.log</code></pre>
<p>I suspect I’ll be required to add a lot of <code>try/catch</code> in most scripts then. One might argue that this is much like when setting <code>set -e</code> in bash. That flag will cause script termination of first non-zero exit code. It also requires using a lot of tricks <code>false || :</code> to circumvent for a line &#8211; but that is also not what it is intended for. The flag is more a special operation mode, where only success is expected, and failure should stop all processing immediately.</p>
<p>A situation like “command not found” would probably be a situation I could live with, being an exception. Instead of general exception use, I would have preferred a result type, that I would then have to inspect if I cared about the exit code. Alternatively, do like <a href="#murex-error-handling-anchor">murex</a> and make it up to the user if an exit code or an exception is desired</p>
<p><br/> <br/></p>
<a name="murex-anchor"></a>
<h1 style="color:DodgerBlue;">
murex
</h1>
<p><a href="https://murex.rocks/">murex</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>I stopped the murex sample short. I ended up spending way to much time getting no where. The documentation do have some minimal usage examples for most components, but I found it difficult to figure out how to combine the individual pieces. The <a href="#nushell-anchor">nushell</a> which shares much spirit with murex, have better examples that made that language more approachable</p>
<p>Shell: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>
<h2 style="color:MediumVioletRed;">
Special features
</h2>
<p>Murex has an pretty neat features backed in. One is <a href="https://murex.rocks/events/">events</a>. This allows to do stuff on e.g. file system changes</p>
<pre class="murex"><code>event onFileSystemChange example=. {
    -&gt; set event
    if { $event.Interrupt.Operation =~ &quot;create&quot; } then {
        git add $event.Interrupt.Path
    }
}</code></pre>
<p>(source: https://murex.rocks/events/onfilesystemchange.html)</p>
<p>It also have baked-in <a href="https://murex.rocks/commands/test.html">unit-testing</a>, <a href="https://murex.rocks/commands/pretty.html">pretty</a> json printing, <a href="https://murex.rocks/commands/struct-keys.html">parsing</a> of json, yaml and toml, and many other nice features Sadly it also have some complications build in. The description of <a href="https://murex.rocks/commands/runmode.html">runmode</a> looks like an advanced topic. Hoping its for special corner cases</p>
<a name="murex-error-handling-anchor"></a>
<h2 style="color:MediumVioletRed;">
Error handling
</h2>
<p>Error handling in murex is dual paradigm. You either choose to get success/fail, or you opt in to get an exception on failure. This is a brilliant idea, and makes so much sense.</p>
<p>For most situations you really on care if a specific command succeeds or not</p>
<pre class="sh"><code>if { code-block } then {
    # true
} else {
    # false
}</code></pre>
<p>(source: https://murex.rocks/commands/if.html)</p>
<p>If you care about the exit-code, murex have a similar concepts as bash, by providing an <a href="https://murex.rocks/commands/exitnum.html">exitnum</a> variable.</p>
<p>Other times, you just need to run a batch of commands, and if any of them fails, then just bail.</p>
<pre class="murex"><code>try {
    out &quot;Hello, World!&quot; -&gt; grep: &quot;non-existent string&quot;
    out &quot;This command will be ignored&quot;
}
catch {
    out &quot;An error was caught&quot;
}</code></pre>
<p>(<em>source: https://murex.rocks/commands/catch.html</em>)</p>
<h2 style="color:MediumVioletRed;">
Command names
</h2>
<p>Question. What does this do?</p>
<pre class="murex"><code>if { g /dev/null } then {
    out &quot;true&quot;
}</code></pre>
<p>Apparently <a href="https://murex.rocks/commands/g.html"><code>g</code></a> is a globbing command. The above example check if the file exists. What could the <a href="https://murex.rocks/commands/f.html"><code>f</code></a> command do? That would be the file object filter function. Other examples are <a href="https://murex.rocks/commands/ja.html"><code>ja</code></a>, <a href="https://murex.rocks/commands/ta.html"><code>ta</code></a> <a href="https://murex.rocks/commands/pt.html"><code>pt</code></a> and <a href="https://murex.rocks/commands/mtac.html"><code>mtac</code></a>… I guess when you know them, the brevity is nice. It also follow the old-hat unix tradition of short abbreviated names, but I am a little worried that too many abbreviation makes writable code, not <em>readable</em> code</p>
<h2 style="color:MediumVioletRed;">
Returning values from functions
</h2>
<p>Just like with the elvish documentation, it is frustratingly difficult to find any documentation on how to return values from a function. There is a tiny indirect example, in the <a href="https://murex.rocks/commands/return.html#examples"><code>return</code></a> documentation, which empathizes on <code>exitnum</code>… What I deduced is that murex also uses stdout for returning data from functions. I then looked at the <a href="https://murex.rocks/commands/out.html"><code>out</code></a> documentation, which then suggests using <a href="https://murex.rocks/commands/tout.html"><code>tout</code></a>, <a href="https://murex.rocks/commands/cast.html"><code>cast</code></a> and <a href="https://murex.rocks/commands/format.html"><code>format</code></a> for data-type return vales. All examples shows json or yaml as return structures. So if wanting something other that text returns, one has to use json, yaml or toml? Some digging around, I later found a data-type <a href="https://murex.rocks/types/">reference</a>. It contains a list of builtin datatypes like bool, int, string, the mentioned structured data and more. The murex repo, actually have some <a href="https://github.com/lmorg/murex/blob/master/examples">examples</a>. Unfortunately none of the examples showed how to use a function… but at least they showed that murex files are suffixed with a ‘.mx’ type name <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f643.png" alt="🙃" class="wp-smiley" style="height: 1em; max-height: 1em;" />. Eventually I found an example in the <a href="https://murex.rocks/user-guide/code-block.html">code block parsing</a> section… although that did not show how to pick up the return value</p>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<h3 id="listing-files">Listing files</h3>
<p>I found no immediate candidates for file listing tools like <code>find</code>. Instead murex provide a globbing tool to do <a href="https://murex.rocks/tour.html#filesystem-wildcards-globbing">filesystem wildcards</a>. I found an example in the rosetta stone <a href="https://murex.rocks/user-guide/rosetta-stone.html#common-one-liners">page</a>.</p>
<pre class="murex"><code>❱ f +d | foreach $dir { out $i }</code></pre>
<pre class="error"><code>Error in `out` (1,14): variable &#39;i&#39; does not exist
                     &gt; Expression: out $i
                     &gt;           :      ^
                     &gt; Character : 5
Error in `out` (1,14): variable &#39;i&#39; does not exist
                     &gt; Expression: out $i
                     &gt;           :      ^
                     &gt; Character : 5
Error in `out` (1,14): variable &#39;i&#39; does not exist
                     &gt; Expression: out $i
                     &gt;           :      ^
                     &gt; Character : 5</code></pre>
<p>Well, it sure is eager to tell you where you went wrong <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> But the error message is actually quite informative, so it was easy to fix the official example</p>
<pre class="murex"><code>f +d | foreach $dir { out $dir }
.git
test</code></pre>
<p>Where <a href="https://murex.rocks/commands/f.html"><code>f</code></a> is filtering, <a href="https://murex.rocks/commands/g.html"><code>g</code></a> is for doing the globbing, but it appears that there is no way to recursively list all sub-directories?</p>
<pre class="murex"><code>❱ g test/**
[
    &quot;test/ untrimmed .doc&quot;,
    &quot;test/A filename with spaces.doc&quot;,
    &quot;test/monzool.net&quot;,
    &quot;test/subdir&quot;,
    &quot;test/ᛗᛟᚾᛉᛟᛟᛚ.net&quot;,
    &quot;test/🄼🄾🄽🅉🄾🄾🄻.net&quot;
]

❱ g test/**/*
[
    &quot;test/subdir/1.dat&quot;,
    &quot;test/subdir/2.dat&quot;,
    &quot;test/subdir/3.dat&quot;
]</code></pre>
<p>I guess its possible to build a command from the building blocks provided, but using the external <code>find</code> command is quite fine</p>
<h3 id="function-arguments">Function arguments</h3>
<p>One would think calling a function with some arguments would be simple. None the less, then I can’t make it work. The <a href="https://murex.rocks/commands/function.html"><code>function</code></a> documentation has <strong>zero</strong> examples of how to do this</p>
<p>Objective: call this function with a path</p>
<pre class="murex"><code>function get_file_list (dir: path) {
    file_list = ${ find $dir -type f }
    out $file_list
}</code></pre>
<p>Like bash?</p>
<pre class="murex"><code>get_file_list &quot;test&quot;</code></pre>
<p>No <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="error"><code>unexpected closing bracket &#39;}&#39;
Expression: }  get_file_list &quot;test&quot;
          :  ^
Character : 1
Error in `./sample.mx` (0,1): exit status 1</code></pre>
<p>Use parentheses?</p>
<pre class="murex"><code>get_file_list (&quot;test&quot;)</code></pre>
<p>No <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> (<a href="https://murex.rocks/parser/single-quote.html">single quoting</a> gave same result)</p>
<pre class="error"><code>find: &quot;test&quot;: No such file or directory
Error in `find` (./sample.mx 1,102):
      Command: find $dir -type f 
      Error: exit status 1</code></pre>
<p>Placing the value in a variable before calling?</p>
<pre><code>set list_dir = &#39;test&#39;
get_file_list ($list_dir)</code></pre>
<p>Yes <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f610.png" alt="😐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Calling with no space between function and argument? …</p>
<pre class="murex"><code>get_file_list(&#39;test&#39;)</code></pre>
<p>Yes</p>
<figure>
<img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/07/Jackie-Chan-WTF.jpg" alt="Jackie-Chan-WTF" />
<figcaption aria-hidden="true">Jackie-Chan-WTF</figcaption>
</figure>
<h3 id="split-a-filename">Split a filename</h3>
<p>I need to split the file extension and name from a file path. In great frustration I can cannot locate any documentation on how to split a string, do substrings, match on things or anything that sounds remotely like something I need.</p>
<p>Could not find a native way to do the splitting, so resorted to use <code>sed</code></p>
<h3 id="data-types">Data types</h3>
<p>How do I create a a variable of a type? I first went to <a href="https://murex.rocks/types/">Data Types</a> section, but that instead appears to be <a href="https://murex.rocks/types/#definitions">describing</a> types that murex can use to interchange data between functions? Okay, found <em>some</em> enlightenment in the section of <a href="https://murex.rocks/parser/">Operators and Tokens</a>. That section lists these options:</p>
<ul>
<li><a href="https://murex.rocks/parser/create-array.html">%[] Create Array</a>: Quickly generate arrays</li>
<li><a href="https://murex.rocks/parser/create-object.html"><code>%{}</code> Create Map</a>: Quickly generate objects and maps</li>
</ul>
<p>(are there slow editions of these then? <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" />)</p>
<h3 id="maps">Maps</h3>
<p>I cannot figure out how to add to a map. There are documented examples where a map magically has manifested and <strong>then</strong> you do <a href="https://murex.rocks/tour.html#using-foreach-loops"><code>formap</code></a> on the data in it &#8211; but how do you add to an existing map?</p>
<p>Lets create a map <code>m</code></p>
<pre><code>❱ m = %{ &quot;net&quot;: [&quot;a&quot;, &quot;b&quot;]}</code></pre>
<p>Use a key and value to add an entry</p>
<pre><code>❱ m[&quot;txt&quot;] = [&quot;c&quot;]</code></pre>
<p>No <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="error"><code>unexpected symbol &#39;[&#39; (91)
Expression: m1[&quot;txt&quot;] = [&quot;c&quot;]
          :   ^
Character : 2</code></pre>
<p><a href="https://murex.rocks/commands/append.html"><code>append</code></a> is only for arrays, but lets try</p>
<pre><code>❱ $m | append %{ &quot;txt&quot; : [&quot;c&quot;]}</code></pre>
<p>No <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="error"><code>[
    &quot;{\&quot;net\&quot;:[\&quot;a\&quot;,\&quot;b\&quot;]}&quot;,
    &quot;{\&quot;txt\&quot;:[\&quot;c\&quot;]}&quot;
]</code></pre>
<p>Using <a href="https://murex.rocks/commands/map.html"><code>map</code></a> gives me some kind of mutant result <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9cc.png" alt="🧌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre><code>❱ map { $m1 } { %{ &quot;txt&quot;: [&quot;c&quot;] } }</code></pre>
<p>No <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/274c.png" alt="❌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="error"><code>{
    &quot;net: [\&quot;a\&quot;,\&quot;b\&quot;]&quot;: &quot;txt: [\&quot;c\&quot;]&quot;
}</code></pre>
<p>Oh well. I decided to throw in the towel here, and abandon the murex sample <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><br/> <br/></p>
<a name="hush-anchor"></a>
<h1 style="color:DodgerBlue;">
Hush
</h1>
<p><a href="https://hush-shell.github.io/">hush</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>Using hush was an absolute pleasure. The language is simple, but I consider that a good thing. It does everything that is needed for scripting. There is perhaps a few lacking features in the standard library, but overall it gets the job done. A stellar feature of hush is command blocks, which allow to run shell commands with capturing output and errors</p>
<p>Documentation is great with complete and precise examples. I had no real issues or frustrations, implementing the sample in hush. Interestingly the bash and hush samples are almost identical, the Hush edition just had much less friction</p>
<p>vscode: https://marketplace.visualstudio.com/items?itemName=hush-vscode.hush</p>
<p>Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>
<h2 style="color:MediumVioletRed;">
About hush
</h2>
<p>Sadly hush appears to be borderline abandonware</p>
<pre class="sh"><code>❱ git log --date=format:&#39;%Y&#39; --pretty=format:&#39;%ad&#39; | sort | uniq -c | awk &#39;{print $2 &quot;: &quot; $1}&#39;
2020: 4
2021: 211
2022: 75
2023: 6
2024: 3</code></pre>
<h2 style="color:MediumVioletRed;">
Installation
</h2>
<p>I installed from cargo <code>cargo install hush</code>, but when parsing arguments using regex I was blocked on this error</p>
<pre class="error"><code>error: &quot;invalid regex&quot; (&quot;regex parse error:\n    --(\\w+)=?(\\w+)?\n       ^^\nerror: Unicode-aware Perl class not found (make sure the unicode-perl feature is enabled)&quot;)</code></pre>
<p>That matches an <a href="https://github.com/hush-shell/hush/issues/38">issue</a> on the hush issue tracker, with a <a href="https://github.com/hush-shell/hush/pull/39">fix</a> merged in May 2023. I then looked at the <a href="https://crates.io/crates/hush">crate</a> and it was last updated in May 14th of 2022. So I cloned the source and build and installed with cargo.</p>
<p>The regex now worked; however, now all my <code>std.print</code> would no longer end with printing a newline, making everything print on one long line. The documentation on the <a href="https://hush-shell.github.io/std.html">standard library</a> did not mention <code>std.println</code>… but it worked. That <a href="https://github.com/hush-shell/hush/issues/22">feature</a> was added May 24th of 2022. I got a bit curious here, on how much had changed in the last two years. In the <em>issues closed</em> section there is only a limited amount of stuff added. Primarily <code>std.println</code> and <code>elseif</code>. In the <em>open issues</em> section there was not a whole lot either. There was this thing though: hush [cannot do filesystem queries] yet. Reported in May 2022, is that hush lacks features like checking if a file exist, if a file is a directory etc.</p>
<p>From the issue responses its clear that the author do not have time to maintain the project &#8211; but its not abandonware. Just recently, a <a href="https://github.com/hush-shell/hush/pull/48">pull request</a> was merged. Anyway, the missing file operations is not a deal breaker. Hush have <a href="https://github.com/hush-shell/hush/issues/20"><em>command blocks</em></a> where we can execute shell code.</p>
<h2 style="color:MediumVioletRed;">
Conditions
</h2>
<p>As far as I understand, hush only accepts single bool expressions as conditions. This means that this is not possible</p>
<pre><code>if value == 1 or value == 42 then</code></pre>
<p>This could a bit lacking if comparing range values, but for most cases this is an okay limitation.</p>
<a name="hush-command-blocks"></a>
<h2 style="color:MediumVioletRed;">
Command blocks
</h2>
<p>I mentioned that hush do not have any file I/O built-in, but <a href="https://hush-shell.github.io/cmd/basic.html">command blocks</a> can be used as a fallback. The ability to just “switch” a section to shell scripting is incredible flexible. Got that magic bash one-liner that generates rainbows <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f308.png" alt="🌈" class="wp-smiley" style="height: 1em; max-height: 1em;" /> and unicorns <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f984.png" alt="🦄" class="wp-smiley" style="height: 1em; max-height: 1em;" />? Just put it in a command block and enjoy</p>
<pre class="hush"><code>function get_file_list(dir)
    let file_list = ${
        find &quot;${dir}&quot; -type f -print0
    }.stdout

    return std.split(file_list, &quot;\0&quot;)
end</code></pre>
<p>If did found one thing I could not get to work with command blocks, and that was to access program arguments. Hush will crash if trying to access any bash <code>$</code> variables. I discovered this, when having failed to find a native way to get the script directory.</p>
<pre class="hush"><code>let script_dir = { dirname &quot;$0&quot; }
std.cd(script_dir)</code></pre>
<p>This crashes hush with a rust error. I find it not to be an unreasonable limitation. Other convenient bash features like arithmetic expansion <code>$(( expression ))</code> is not supported either in command blocks.</p>
<h2 style="color:MediumVioletRed;">
Dictionaries
</h2>
<p>Dictionaries access is a bit inconsistent on the keys. Initialization do not accept string type as keys, so any key with space or dash is not possible</p>
<pre class="hush"><code>let dict = @[
    &quot;key 1&quot;: nil,
    &quot;key-2&quot;: nil
]</code></pre>
<p>Both key/value pairs above give compile errors.</p>
<pre class="hush"><code>Error: (line 9, column 4) - unexpected &#39;&quot;key 1&quot;&#39;, expected identifier
Error: (line 10, column 4) - unexpected &#39;&quot;key-2&quot;&#39;, expected identifier</code></pre>
<p>However assign and access is possible with both spaces and dashes</p>
<pre class="hush"><code>dict[&quot;key 1&quot;] = &quot;space&quot;
dict[&quot;key-2&quot;] = &quot;dash&quot;</code></pre>
<p>Speaking of dictionaries, how do you remove an entry from it? Don’t think its possible. Setting as <code>nil</code> value is only option, as far as I could find.</p>
<h2 style="color:MediumVioletRed;">
Program arguments
</h2>
<p>Hush can give you a list of arguments with <code>std.args</code>, but otherwise it offers no argument parsing features. I then set in motion to do a generic argument parser with validation and value extraction. It was fun and hush was quite capable. Then it dawned on me, that this was a false approach. If hush have had a parsing library, I would have used that, but I didn’t implement an argument parser in bash, so I shouldn’t do that in any of the other languages. That would also make them look unfair larger that the bash edition. When I scrapped the parser, I found that handling only the specific arguments, made the hush code look very similar to the original bash code.</p>
<h2 style="color:MediumVioletRed;">
Printing
</h2>
<p>Color printing using ansi codes did not work. I could not get <code>std.print</code> to handle escape sequences for setting colors, so I had to fallback to command blocks to make this work. That lead to another issue. Mixing the native <code>std.print</code> and <code>echo</code>/<code>printf</code> in command blocks do not work well together, due to output stream flushing issues. That is perhaps not unexpected.</p>
<h2 style="color:MediumVioletRed;">
Error handling
</h2>
<p>Printing dictionaries gives a pretty good idea on what is in it, however when printing an error, it will give output like this</p>
<pre class="error"><code>error: &quot;command returned non-zero&quot; (@[ &quot;stderr&quot;: &quot;&quot;, &quot;stdout&quot;: &quot;&quot;, &quot;error&quot;: @[ &quot;pos&quot;: &quot;\u{1b}[38;5;2m./sample.hsh\u{1b}[39m (line 109, column 8)&quot;, &quot;status&quot;: 1 ] ])</code></pre>
<p>There are actually two hidden keys in the dictionary. The value <em>“command returned non-zero”</em> , which is a textual message of the error, is in a key <code>description</code>. The rest of the printed content is in a key <code>context</code></p>
<pre class="hush"><code>if (std.has_error(search_result)) then
    std.println(&quot;Description: &quot; ++ search_result.description)
    std.println(&quot;Status: &quot; ++ std.to_string(search_result.context.error.status)))</code></pre>
<p>It is <a href="https://hush-shell.github.io/cmd/basic.html#errors">documented</a>, just not very obvious</p>
<h2 style="color:MediumVioletRed;">
Compiler
</h2>
<p>Compile errors was pretty good and its reasonable easy to find the culprits from its messages. A lot better that bash</p>
<pre class="hush"><code>if std.contains(arg, &quot;--list-dir=&quot;) then</code></pre>
<pre class="error"><code>Panic in ./sample.hsh (line 28, column 23): value (&quot;--list-dir=&quot;) has unexpected type, expected character</code></pre>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<p>I didn’t make any notes during development of the Hush sample, as there wasn’t really any issues to write about</p>
<p>I only had these obstacles during the process 1) I could not find a way to change the current directory to that of the script 2) I could not find a easy way to do generate random numbers 3) Ansi escape codes do not work for setting color. Had to use <code>tput</code> 4) How to extract error context values is not obvious</p>
<p><br/> <br/></p>
<a name="koi-anchor"></a>
<h1 style="color:DodgerBlue;">
Koi
</h1>
<p><a href="https://koi-lang.dev/">koi</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>Writing Koi code was pretty much just like writing Hush. It was quite straight forward to write the sample as the language is simple and intuitive. The simple interop with external commands is excellent. There are sadly three significant pain points, that will make me not pick up Koi</p>
<ol type="1">
<li>Program options are not supported</li>
<li>On coding mistakes, the compiler generally prints a completely generic message</li>
<li>Appears abandonware</li>
</ol>
<p>Documentation was fine, but for the most part implicit from examples</p>
<p>Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆<br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆</p>
<h2 style="color:MediumVioletRed;">
About Koi
</h2>
<p>Where most other script languages use <code>$</code> for referencing variables (<code>$var</code>), in Koi variables are just referenced by there name (<code>var</code>). For string interpolation variables are referenced in <code>{}</code> (<code>{var}</code>).</p>
<p>Koi also takes a more object oriented approach where variables have methods.</p>
<pre class="koi"><code>print(&#39;koi&#39;.upper().split(&#39;o&#39;))</code></pre>
<p>Sadly it appears to be abandonware</p>
<pre class="sh"><code>❱ git log --date=format:&#39;%Y&#39; --pretty=format:&#39;%ad&#39; | sort | uniq -c | awk &#39;{print $2 &quot;: &quot; $1}&#39;
2020: 134
2021: 181
2022: 11
2023: 1</code></pre>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<h3 id="no-script-arguments">No script arguments</h3>
<p>It is not documented, and I found no way to do it… Koi scripts cannot receive program arguments <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Instead of the script receiving arguments, the Koi interpreter itself would hijack the arguments</p>
<pre class="sh"><code>❱ ./sample.koi --list-dir=test
error: Found argument &#39;--list-dir&#39; which wasn&#39;t expected, or isn&#39;t valid in this context

USAGE:
    koi [FLAGS] [OPTIONS] [PATH]

For more information try --help</code></pre>
<p>That is somewhat of a major bummer <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2639.png" alt="☹" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="no-errors">No errors</h3>
<p>Made a mistake, and referenced an non-existing variable</p>
<pre class="diff"><code>fn get_file_list(dir) {
    let files = $(
        find {dir} -type f -print0
    ).split(&#39;\0&#39;)
-   return file_list
+   return files
}</code></pre>
<p>This gives <strong>no</strong> indication of error <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="get-list-of-files">Get list of files</h3>
<p>Koi will not split on <code>\0</code> so had to change the <code>find</code> command to separate by newlines</p>
<pre class="diff"><code>fn get_file_list(dir) {
    let files = $(
-       find {dir} -type f -print0
+       find {dir} -type f
-   ).strip().split(&#39;\0&#39;)
+   ).strip().split(&#39;\n&#39;)
    return files
}</code></pre>
<h3 id="append-to-array">Append to array</h3>
<p>How to append values to an array is not documented. Figured out this would work</p>
<pre class="koi"><code>let a = [1]
a += [2]

#=&gt; [1, 2]</code></pre>
<h3 id="color-printing">Color printing</h3>
<p>I could not get any color printing to work with Koi’s built in <code>print</code> function. Neither would <code>printf</code> or echo work with ansi codes, however just like Hush, <code>tput</code> would work <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3a8.png" alt="🎨" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
<h2 id="error-messages">Error messages</h2>
</h2>
<p>As mentioned, Koi do not always detect mistakes, but when it does, the errors messages are lacking detail</p>
<pre class="error"><code>thread &#39;main&#39; panicked at src/parser/stmt.rs:163:13:
expected right brace
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace</code></pre>
<p>(adding <code>RUST_BACKTRACE=1</code> only reveal details of Koi internals, and nothing of the script generating the error)</p>
<p>This is the extend of message from pretty much any mistake you can make in Koi (non-existing variable or function, syntax mistake etc). This makes mistakes pretty hard to locate <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="koi"><code>fn blue() { tput setaf 4 }</code></pre>
<pre class="error"><code>thread &#39;main&#39; panicked at src/parser/stmt.rs:61:30:
only assignment, call and command expressions are allowed as statements
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace</code></pre>
<p>This is not helpful &#8211; especially when you have added many lines since last run.</p>
<p>Btw the fix is this</p>
<pre class="koi"><code>fn blue() {
    tput setaf 4
}</code></pre>
<p><br/> <br/></p>
<a name="abs-anchor"></a>
<h1 style="color:DodgerBlue;">
abs
</h1>
<p><a href="https://www.abs-lang.org/">abs</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>Creating the sample was very easy. As I made the Koi sample before this, the abs edition was more or less a copy paste. Compared to Koi, the abs language have a few more features and makes for a more complete language. The printing was the weakest point of abs, but otherwise the experience was very good. It’s a shame it appears to be abandonware</p>
<p>vscode: https://marketplace.visualstudio.com/items?itemName=abs-lang.vscode-abs</p>
<p>Repl: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆<br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>
<h2 style="color:MediumVioletRed;">
Returning values from functions
</h2>
<p>Let me praise abs here. It actually documents, in plain sight, <a href="https://www.abs-lang.org/syntax/return/">how to return values from functions</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<h3 id="program-arguments">Program arguments</h3>
<p>abs have a nice <a href="https://www.abs-lang.org/stdlib/cli/">cli library</a> for making it easier to make cli tools that take arguments. Unfortunately it seems to be in the format <code>script &lt;command&gt; [option]</code> only. I need <code>script [option]</code>. For convenience, I decided to use it anyway as <code>./sample.abs run [option]</code></p>
<h3 id="listing-files-1">Listing files</h3>
<p>Like most of the other languages (bash and hush being the exceptions), abs would not split on null terminations</p>
<pre class="diff"><code>-files = `find $dir -type f -print0`.split(&#39;\0&#39;)
+files = `find $dir -type f`.lines()</code></pre>
<p>The <a href="https://www.abs-lang.org/types/string/#lines">lines</a> was convenient. Then I also didn’t have to pop the last empty item from a split <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="printing-without-newline">Printing without newline</h3>
<p>I cannot make <code>echo</code> <strong>not</strong> add a newline. It seems not to be a feature. This is especially annoying when having to print background colors… Discovered in https://github.com/abs-lang/abs/issues/374 that I could see that <code>echo</code> has more options, but the non-newline printing appears to be still on the wish list</p>
<h3 id="color-printing-1">Color printing</h3>
<p>I cannot find any way to make color printing work properly</p>
<p><code>echo</code> with ansi codes just print the uninterpreted text</p>
<pre class="abs"><code>echo(&quot;$prefix \e[34m $text \e[0m&quot;)</code></pre>
<p>An all bash command do the same</p>
<pre class="sh"><code>`printf &quot;$prefix \e[34m %s \e[0m&quot; &quot;$text&quot;`</code></pre>
<p>What <em>sorta</em> work is using <code>tput</code>, however as I could not find any way to print without newline, lines with background color will color the background until next line could reset the color. Mixing bash <code>printf</code> and abs <code>echo</code> do not (unsurprising) go well, so had to settle for fully colored lines or no colors at all <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
Error messages
</h2>
<p>I accidentally did <code>.[</code> instead of <code>[</code> and got a parsing fail stack back</p>
<pre class="abs"><code>if categorized_files.[extension] == null {</code></pre>
<pre class="error"><code> parser errors:
        expected next token to be [, got IDENT instead
        [46:30]         if categorized_files.[extension] == null {
        no prefix parse function for &#39;]&#39; found
        [46:40]         if categorized_files.[extension] == null {
        no prefix parse function for &#39;==&#39; found
        [46:42]         if categorized_files.[extension] == null {
        expected next token to be ), got IDENT instead
</code></pre>
<p>I guess the error message could have been more precise, but it was perfectly easy to figure out what the mistake was. So served its purpose <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>However more than once I ended up with something like this, which could be somewhat hard to locate</p>
<pre class="error"><code>bash: -c: line 1: unexpected EOF while looking for matching ``&#39;</code></pre>
<p><br/> <br/></p>
<a name="nushell-anchor"></a>
<h1 style="color:DodgerBlue;">
nushell
</h1>
<p><a href="https://www.nushell.sh">nushell</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>Diving into nushell was quite a thrilling and enjoyable experience. Nushell is quite different from bash, and I learned a lot from my small encounter.</p>
<p>Documentation is very good. There is an official book <a href="https://www.nushell.sh/book/thinking_in_nu.html">Thinking in Nushell</a>, <a href="http://www.nushell.sh/cookbook/">cookbook</a> a great <a href="https://www.nushell.sh/commands/">command overview</a> and a <a href="https://www.nushell.sh/lang-guide/">language reference guide</a>. What sets nushell apart from perhaps elvis and murex, is that nushell appears relatively broadly adopted, and it is possible to find a reasonable amount of blog posts and articles to get one started or find inspiration.</p>
<p>Nushell can be a little perplexing initially. When coming from bash, nushell can do so very much more. One thing that was especially mystifying to me, was how the data exchange between commands worked. Then found the <a href="https://www.nushell.sh/contributor-book/commands.html">commands</a> section in the <a href="https://www.nushell.sh/contributor-book/">contributor book</a>. Commands communicate using a data structure <a href="https://docs.rs/nu-protocol/latest/nu_protocol/enum.PipelineData.html">PipelineData</a>. That really made it click for how nushell can operate as it do.</p>
<p>There is lots to learn when switching from bash to nushell, but I think nushell is tremendously powerful when it comes to data manipulation. I see a lot of of situations where my toolbox of grep, sed, awk and other old-hat tools could be replaced by nushell.</p>
<p>vscode language plugin: https://marketplace.visualstudio.com/items?itemName=TheNuProjectContributors.vscode-nushell-lang</p>
<p>Shell: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>
<h2 style="color:MediumVioletRed;">
Function return values
</h2>
<p>“Funny” enough, just like for the other shell-first languages elvish, murex and ion, I found no documentation on how to return values from a <a href="https://www.nushell.sh/book/custom_commands.html">nushell function</a>. There is documented something about function [type signatures](https://www.nushell.sh/book/command_signature.html that also covers function return types. Actually functions in nushell are called <em>commands</em> and appear more to resemble, calling a subscript in bash. It seems that commands stream output (to stdout and/or stderr) and can then be picked up by the next command. Like murex (and perhaps also elvish?) this steam can be formatted to different types</p>
<p>I think what makes these “shell first, scripting second” projects a little difficult to consume from a scripting angle, is that most commands are documented like they would be used in a shell.</p>
<p>One example is the <a href="https://www.nushell.sh/commands/docs/complete.html"><code>complete</code></a> command. I stumbled upon <code>complete</code> when reading about capturing <a href="https://www.nushell.sh/book/stdout_stderr_exit_codes.html#using-the-complete-command">exit-codes</a>.</p>
<pre class="nu"><code>&gt; cat unknown.txt | complete
╭───────────┬─────────────────────────────────────────────╮
│ stdout    │                                             │
│ stderr    │ cat: unknown.txt: No such file or directory │
│ exit_code │ 1                                           │
╰───────────┴─────────────────────────────────────────────╯</code></pre>
<p>It wasn’t immediate clear to me how that transitioned to be used in a script, but I was lucky to find an example on an external site, a <a href="https://jpospisil.com/2023/05/25/writing-shell-scripts-in-nushell">blogpost</a> by <a href="https://jpospisil.com/">Jiri Pospisil</a>:</p>
<pre class="nu"><code>let response = (curl --fail -s $url | complete)

match $response.exit_code {
  0 =&gt; {
    $response.stdout | jq ...
  },
  22 =&gt; {
    print -e $&quot;Project \&quot;($name)\&quot; not found or server error!&quot;
    exit 1
  },
  - =&gt; {
    print -e &quot;It&#39;s all broken.&quot;
    exit 1
  }
}</code></pre>
<p>This example shows how to:</p>
<ul>
<li>call an external program</li>
<li>capture the response</li>
<li>evaluate the exit code</li>
</ul>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Documentation for nushell is otherwise quite good. Nushell have an incredible amount of features, but it is very well presented. Nushell even support <a href="https://www.nushell.sh/book/working_with_strings.html#coloring-strings">colors</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f308.png" alt="🌈" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The nushell introduction have this quote, which I think it true. Nushell is very different from bash</p>
<p><em><strong>Thinking in Nushell</strong>: If you’re used to using mutable variables for different tasks, it will take some time to learn how to do each task in a more functional style.</em></p>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<h3 id="types">Types</h3>
<p>I wondered why I got a type error when attempting to filter <code>file_list</code>. Found the <a href="https://www.nushell.sh/commands/docs/describe.html"><code>describe</code></a> function for revealing the underlying type</p>
<pre class="nushell"><code>let file_list = (ls &quot;./test&quot; | where type == file)
$file_list | describe

=&gt; table&lt;name: string, type: string, size: filesize, modified: date&gt;</code></pre>
<p>Oh. I was attempting to stream a table into something that expected a list. So how to convert the table to a list?</p>
<p>This is one way to do it</p>
<pre class="nushell"><code>let file_paths = $file_list | get name
$file_paths | group-by { path parse | get extension }</code></pre>
<h3 id="getting-a-list-of-files">Getting a list of files</h3>
<p>I am not getting files from sub-directories from my <code>ls ./test</code>. Nushell <code>find</code> is something else, so it is not really clear how to find a three of files? It required a visit to stack-overflow to find out that globbing can be used. <code>ls ./test/**/*</code>. I kind of like it <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f44d.png" alt="👍" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Update: found the documentation in the <a href="https://www.nushell.sh/book/coming_from_bash.html">bash comparison sheet</a></p>
<h3 id="iterating-a-record">Iterating a record</h3>
<p>I have a record of the file categories. I cannot figure out how to extract key value pairs from this</p>
<pre class="nushell"><code>let file_categories = $file_list | group-by { path parse | get extension }
$file_categories | describe

#=&gt; record&lt;doc: list&lt;string&gt;, net: list&lt;string&gt;, dat: list&lt;string&gt;&gt;</code></pre>
<p>Lots of reading later: records should be <a href="https://www.nushell.sh/book/types_of_data.html#records">transposed to tables</a>, before iterating them.</p>
<h3 id="functions">Functions</h3>
<p>Functions in nushell are called <em>commands</em>.</p>
<p>nushell is a typed language, so function parameters can optionally be annotated with types. For the printing functions I want to pass in another function to do color handling. From the <a href="https://www.nushell.sh/book/custom_commands.html#parameter-types">parameter types list</a> I guess <code>closure</code> is the best fit. I could however not find any documentation on that type directly.</p>
<p>I am a little confused on how commands work. This example do not act like I would expect</p>
<pre class="nushell"><code>def print_category [extension: string, filenames: list] {
    echo &quot;Category: ($extension)&quot;
}
print_category &quot;dat&quot; [&quot;1&quot;, &quot;2&quot;]

#=&gt; Category: ($extension)</code></pre>
<p>The parameter value is not expanding? I noticed the command examples all use string interpolation.</p>
<pre class="nushell"><code>def greet [name] {
  $&quot;hello ($name)&quot;
}
greet nushell

#=&gt; hello nushell</code></pre>
<p>Using <code>echo</code> or <code>print</code> appears not to work in commands, while interpolation do?</p>
<pre class="nushell"><code>def print_category [extension: string, filenames: list] {
    print &quot;Category: ($extension)&quot;
    $&quot;Category: ($extension)&quot;
}
print_category &quot;dat&quot; [&quot;1&quot;, &quot;2&quot;]

#=&gt; Category: ($extension)
    Category: dat</code></pre>
<p><code>print</code> do not return anything, but <code>echo</code> acts more like an identity function, so I would have thought <code>echo</code> would have worked?</p>
<p>Aha! This worked</p>
<pre class="diff"><code>```nushell
def print_category [extension: string, filenames: list] {
-   print &quot;Category: ($extension)&quot;
+   $extension | print
    $&quot;Category: ($extension)&quot;
}</code></pre>
<p>Ohh. Maybe I understood nothing in regard to printing. This also works</p>
<pre class="nushell"><code>print $&quot;Category: ($extension)&quot;</code></pre>
<p>At this point, I am not entirely sure how things work. Its nothing like anything that I am familiar with. Learning nushell feels a lot like being educated <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="filter-to-filename-only">Filter to filename only</h3>
<p>From the categorizing, I got files with full path and extension, but should on have filename for each extension</p>
<pre class="diff"><code>-let file_categories = $file_list | group-by { path parse | get extension }
+let file_categories = $file_list | path basename | group-by { path parse | get extension }</code></pre>
<p>This makes data change from (e.g.) <em>path/filename.ext</em> to <em>filename.ext</em> Now how do I cut the extension from the data, but still be able to do grouping by the same cut data?</p>
<p>Opted to just split it in two operations. First group by extension, and then <code>get stem</code> when needing the filename only.</p>
<h3 id="passing-in-a-printing-function">Passing in a printing function</h3>
<p>Was okay easy to find out that <a href="https://www.nushell.sh/commands/docs/do.html"><code>do</code></a> is required, just by browsing the command list</p>
<h3 id="random-value">Random value</h3>
<p>Nushell have native support for several <a href="https://www.nushell.sh/commands/categories/random.html">random number generators</a>. The integer random generator fit perfectly here. Only this is that the examples are all inclusive ranges</p>
<pre class="nushell"><code>random int 1..10</code></pre>
<p>First attempt was just to do <code>random int 1..(10-1)</code>, but that gave syntax error. The command documentation of <a href="https://www.nushell.sh/commands/docs/range.html">range</a> is quite sparse. After some digging around, I found a chapter in the nushell book about <a href="https://www.nushell.sh/book/types_of_data.html#inclusive-and-non-inclusive-ranges">ranges</a> which offered some details</p>
<pre class="nushell"><code>random int 1..&lt;10</code></pre>
<p>Thats actually amazing <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f929.png" alt="🤩" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>I also figured out that my attempt of subtracting (<code>random int 1..(10-1)</code>) actually was possible if using proper syntax <code>random int 1..(10 - 1)</code>. That was a <strong>me</strong> error, not nushell <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="handling-grep-errors">Handling grep errors</h3>
<p>It was relatively easy to find out how to handle errors from called external programs. Seems that I have two options.</p>
<p>Use exceptions</p>
<pre class="nushell"><code>try { 
    grep $text $filename
    true
} catch { 
    false 
}</code></pre>
<p>Or, better to my preference, check the exit code</p>
<pre class="nushell"><code>let result = grep $text $filename | complete
return ($result.exit_code == 0)</code></pre>
<p>That could then be rewritten to</p>
<pre class="nushell"><code>(grep $text $filename | complete | get exit_code) == 0</code></pre>
<h3 id="program-arguments-1">Program arguments</h3>
<p>Took me a while to figure out how to get arguments. It wasn’t until <a href="https://github.com/nushell/nushell/issues/11035">this</a> bug report, that I realized you just make a <code>main</code> function. You then combine that with command <a href="https://www.nushell.sh/book/custom_commands.html#flags">flags</a>.</p>
<p>Update: scripts with <code>main</code> is documented <a href="https://www.nushell.sh/book/scripts.html#parameterizing-scripts">here</a></p>
<p>Really weird. I cannot get <code>main</code> to work. It is never called. I have put this at the top of my sample file, but it never prints</p>
<pre class="nushell"><code>#!/usr/bin/env nu

def main [input: string] {
    $&quot;Ere I am, J.H.&quot; | print
    print $input
}</code></pre>
<p>The second weird thing is, that if I put the same code in another file, then it works fine!</p>
<p>Figured it out, when I put a print on the top level also</p>
<pre class="nushell"><code>#!/usr/bin/env nu

def main [input: string] {
    $&quot;Ere I am, J.H.&quot; | print
    print $input
}

&quot;He&#39;s got away from us, Jack&quot; | print</code></pre>
<p>When run, it gives this output</p>
<pre class="sh"><code>❱ ./brazil.nu &quot;And this is my receipt for your receipt&quot;

He&#39;s got away from us, Jack
Ere I am, J.H.
And this is my receipt for your receipt</code></pre>
<p>So it runs all top level code before running main. That explain why it looked like it never worked in my sample code. To test out <code>main</code> and not needing to comment out all code, I just put a <code>return</code> after <code>main</code>.</p>
<pre class="nushell"><code>def main [] {
}
return</code></pre>
<p>Hence, the first thing the script will do is to return (and then exit) <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f0cf.png" alt="🃏" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="builtin-usage-help">Builtin usage help</h3>
<p>It is very clever how the command arguments can auto-generate a help message. For nushell I then did not have to do this myself &#8211; its baked in <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9c1.png" alt="🧁" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="sh"><code>./sample.nu -h
Usage:
  &gt; main {flags} 

Flags:
  --list-dir &lt;String&gt; - Directory to list (default: &#39;test&#39;)
  --no-color - Disable color output. (default: false)
  -h, --help - Display the help message for this command</code></pre>
<p>Though its a shame that it is listing the command name, and not the script name</p>
<pre class="nushell"><code>Usage:
  &gt; main {flags} </code></pre>
<p>This should have said <code>sample.nu</code> instead, as <code>main</code> make no sense outside the script. I found no way to change this <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>I also wished that the description texts were aligned… but thats a nitpick</p>
<h3 id="getting-a-list-of-files-take-2">Getting a list of files, take 2</h3>
<p>After getting program arguments working, I wanted to pass in the user specified path to <code>ls</code> instead of the hardcoded value.</p>
<pre class="diff"><code>-let file_list = (ls ./test/**/* | where type == file) | get name
+let file_list = (ls ./$list_dir/**/* | where type == file) | get name</code></pre>
<p>This do now work</p>
<pre class="error"><code>let file_list = (ls ./$list_dir/**/* | where type == file) | get name
                     ─────────┬────────
                              ╰── Pattern, file or folder not found</code></pre>
<p>I couldn’t figure out a way to format this input to ls in a way that would not generate that error, as <code>ls</code> do not support string input. I tried <a href="https://www.nushell.sh/commands/docs/glob.html"><code>glob</code></a> also, but that returned an empty list when using a variable argument</p>
<p>I was unable to find any combination of <code>ls</code>, <code>glob</code> <code>path parse</code> and would compile or give the expected answer. I’m sure the is one, but I was just not able to find it <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f44e.png" alt="👎" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>After a lot of trail and error, I finally figured it out using <code>glob</code>.</p>
<pre class="nushell"><code>let glob_list = (glob $&quot;($list_dir)/**/*&quot;)</code></pre>
<p>I quickly discovered that <code>glob</code> returns full path items, instead of relative paths <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92c.png" alt="🤬" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>I eventually found a suggestion to just use the external find command: https://superuser.com/a/1721043. Sadly, falling back to use the external <code>find</code> command was also not without its own pains…</p>
<pre class="diff"><code>- let glob_list = (glob $&quot;($list_dir)/**/*&quot;)
- let file_list = $glob_list | where ($it | path type) == file
+^find test -type f</code></pre>
<p>Apparently the format returned from the find command, did not sit well with next functionality</p>
<pre class="error"><code>Error:   × Main thread panicked.
  ├─&#x25b6; at /Users/brew/Library/Caches/Homebrew/cargo_cache/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/rng.rs:134:9
  ╰─&#x25b6; cannot sample empty range
  help: set the `RUST_BACKTRACE=1` environment variable to display a backtrace.</code></pre>
<p>Inspecting the output from <code>^find</code>, revealed that nushell seem to interpret all output as one single entry. How to split into individual file items then?</p>
<p>Tried all <code>path</code> and <code>split</code> incarnations I could imagine, but all gave me all files as a single entity</p>
<pre class="nushell"><code>^find test -type f | path parse
^find test -type f -print0 | split list &#39;\0&#39;
^find test -type f | split list &#39;\n&#39;
^find test -type f | split row &#39;\n&#39;</code></pre>
<p>I was approaching a solution (I think)</p>
<pre class="nushell"><code>^find $list_dir -type f | complete | get stdout</code></pre>
<p>But I went for the interweb, and thanx to the <a href="https://wiki.archlinux.org/title/Nushell#Input_from_external_programs">Arch wiki</a>, I finally had the solution</p>
<pre class="nushell"><code>^find test -type f | lines</code></pre>
<p>Oh man. So easy &#8211; when you know how <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
Documentation
</h2>
<p>The reference page for each command, provide only the simplest examples. For more advanced/real usage, the explaining is done elsewhere. This make sense if reading as a book, but do not work very well as a reference. Some inter-linking or tags would have helped.</p>
<h2 style="color:MediumVioletRed;">
Error message
</h2>
<p>Error messages are really good, and point directly to where the error is, and what is wrong</p>
<pre class="error"><code> 69 │     let l = (glob $list_dir | path parse)
    ·              ──┬─
    ·                ╰── value originates from here
 70 │     $l | path type | print
    ·          ────┬────
    ·              ╰── expected: string, row or list
 71 │     let file_list = []</code></pre>
<p>… that is, unless you you assign the output, then you also mute errors <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="nushell"><code>let files = $file_list | where (it | path type) == &quot;File&quot;</code></pre>
<p>No errors. Lets remove the redirect</p>
<pre class="errofile_listr"><code>$ | where (it | path type) == &quot;File&quot;
·                   ─┬
·                    ╰── Command `it` not found
╰────
  help: Did you mean `bits`?</code></pre>
<p><br/> <br/></p>
<a name="ysh-anchor"></a>
<h1 style="color:DodgerBlue;">
ysh
</h1>
<p><a href="https://www.oilshell.org/release/latest/doc/ysh-tour.html">ysh</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>Is feels more like an evolution of an existing language that is very much bash, but not bash. It has less footguns, many more features, but is equally complicated &#8211; just not from quirks, but from feature complexity. I found it to be pretty confusing to differentiate between when it was bash, Osh or Ysh. Then there is the very different syntax/semantics of <code>proc</code> and <code>func</code> which seem unnecessary (see Hush) and just adds to the confusion.</p>
<p>It’s a shame that the oilshell site do not have section anchors, so that when you are on a section, you can copy the link (you have to find the entry in the top of the pages, and copy the TOC link). But its a minor/pedantic nitpick.</p>
<p>I am sure you could write a rocket launcher in Ysh, but I am less confident that it is appropriate for the rare shell script need. It is for the advanced scripting needs</p>
<p>Shell: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆<br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆</p>
<h2 style="color:MediumVioletRed;">
It’s complicated
</h2>
<p>I have a bit hesitation on ysh. To me ysh appears to cater shell experts to be the most flexible multi tool for any purpose. This can be great, but also makes it… complicated</p>
<p>There are many things in ysh that makes me think there is a relatively high learning curve, and a touch of a “<em>writable language, not readable language</em>”</p>
<p>How to make an array. Simple <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f44d.png" alt="👍" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre><code>var foods = [&#39;ale&#39;, &#39;bean&#39;, &#39;corn&#39;]</code></pre>
<p>How <strong>also</strong> to make an array, if that array <em>is of strings</em></p>
<pre><code>var foods = :| ale bean corn |</code></pre>
<h3 id="functions-1">Functions</h3>
<p>Ysh divide functions into two modes: <em>command</em> and <em>expression</em>.</p>
<p><em>Commands</em> are units of shell-like functions which operates on exterior boundaries. Commands are defined by the <code>proc</code> keyword and should use <em>kebab-casing</em>.</p>
<p><em>Expressions</em> are more similar to python functions, and operate only on internal values as pure functions. Expressions are defined by the <code>func</code> keyword and use <code>camelCasing</code>.</p>
<p>This is a fantastic separation, which enforces a clear distinction between external functionality, and native script logic (similar to the concept in <a href="#hush-command-blocks">Hush</a>)</p>
<p>I think where it gets complicated, is how to call func’s and especially proc’s; that is also why there is a rather large documentation on how to call these: <a href="https://www.oilshell.org/release/latest/doc/proc-func.html">Guide to Procs and Funcs</a></p>
<p>Here is an example from the guide… We have a proc <code>my-cd</code>. Lets call <code>my-cd</code> with <strong>two</strong> arguments</p>
<pre class="ysh"><code>my-cd /tmp {
  echo $PWD
  echo hi
}</code></pre>
<p>When we look at the definition of <code>my-cd</code>, we notice that it has <strong>four</strong> parameters</p>
<pre class="ysh"><code>proc my-cd (dest; ; ; block) {
  cd $dest (; ; block)
}</code></pre>
<p>This is because proc’s have four types of parameters. As described in the guide:</p>
<pre class="ysh"><code>proc p (
    w1, w2, ...rest_word;     # word params
    p1, p2, ...rest_pos;      # pos params
    n1, n2, ...rest_named;    # named params
    block                     # block param
) {
  eval (block)
}</code></pre>
<p>So you can call the above proc like this</p>
<pre class="ysh"><code>var pos_args = [3, 4]
var named_args = {foo: &#39;bar&#39;}
p /bin /tmp (1, 2, ...pos_args; n1=43, ...named_args; { echo &quot;hi&quot; })</code></pre>
<p>Btw, in the <code>my-cd</code> example, I am still not sure what the block part in the body ( <code>cd $dest (; ; block)</code>) does? The documentation says <code>()</code> is an expression. Ysh have <code>cd</code> as a builtin function (<a href="https://www.oilshell.org/release/latest/doc/ref/chap-builtin-cmd.html#ysh-cd">ysh-cd</a>) which takes a block as second argument. Not sure how <code>(; ; block)</code> becomes a block… <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>It appears that ysh has twofold implementation of most features. One with command syntax, and another using expression syntax? This speaks very much into the split between the two modes, but also complicates things as everything now has… well, two syntax’es</p>
<p>I was initial very exited about these modes, that divided concepts. Reality is that its like to different languages mixed together, both in code and in my head <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f939.png" alt="🤹" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Frankly, I hate it <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Guess this last thing is just an observation. Ysh have three usage of <code>...</code>: &#8211; <code>...</code> as the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax"><em>spread operator</em></a>. &#8211; <code>...</code> as the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters"><em>rest parameters</em></a> &#8211; <code>...</code> as a prefix that allows to split long commands over multiple lines</p>
<h3 id="shopt">shopt</h3>
<p>It has some of the same flags (<code>shopt</code>) like bash to modify runtime behaviour. I never really liked that part of bash. It’s an obtuse feature, but at least Ysh allows to contain it to only just blocks of code</p>
<pre class="ysh"><code>shopt --unset errexit {  # ignore errors
  cp ale /tmp
  cp bean /bin
}</code></pre>
<p>(<em>source: https://www.oilshell.org/release/latest/doc/ysh-tour.html</em>)</p>
<h3 id="error-handling">Error handling</h3>
<p>The developer of ysh has a remarkable <a href="https://www.oilshell.org/release/latest/doc/error-handling.html">insight</a> in bash error handling and <a href="https://www.oilshell.org/release/latest/doc/error-handling.html#disabled-errexit-quirk-if-myfunc-pitfall">quirks</a> related to <a href="https://www.oilshell.org/release/latest/doc/error-handling.html#the-meta-pitfall">bash <code>$?</code></a> error handling. ysh takes a firm stance on failing on every error. It then provide you with options to specifically handle errors</p>
<pre class="ysh"><code>try {
  ls /bad
  grep fail /also_bad
}
case (_status) {
  (0)    { echo &#39;found&#39; }
  (1)    { echo &#39;not found&#39; }
  (else) { echo &quot;grep returned status $_status&quot; }
}</code></pre>
<p>This will exit the <code>try</code> block on first error. The <code>_status</code> is the ysh equivalent of bash <code>$?</code> I have some thoughts about this &#8211; Looks like one can just ignore errors by not looking at <code>_status</code> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f44d.png" alt="👍" class="wp-smiley" style="height: 1em; max-height: 1em;" /> &#8211; Is <code>_status</code> a global thing? I don’t think background processes can overwrite this, as there is a <a href="https://www.oilshell.org/release/latest/doc/ref/chap-special-var.html#_process_sub_status">_process_sub_status</a> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> &#8211; Which of the commands in the <code>try</code> block failed? Hush uses a <a href="https://hush-shell.github.io/cmd/basic.html#errors"><code>context</code> array</a> to indicate which command failed.</p>
<p>Another example is a comment on how some programs signal usage errors with an exit code other than 0 (success) or 1 (failure), but the shell will just interpret non-zero exit codes as operation error</p>
<pre class="sh"><code>if grep &#39;class\(&#39; *.py; then  # grep syntax error, status 2
  echo &#39;found class(&#39;
else
  echo &#39;not found is a lie&#39;
fi</code></pre>
<p>In practice I think this is fine. For the most part with shell scripting we just need to know if it succeeds or fails. If we care about the exit code, check the exit code explicitly.</p>
<p>In fact this is such a common situation that ysh have a special <a href="https://www.oilshell.org/release/latest/doc/ref/chap-builtin-cmd.html#boolstatus"><code>boolstatus</code></a> operator for this</p>
<pre class="ysh"><code>if boolstatus egrep &#39;[0-9]+&#39; myfile {  # may abort
  echo &#39;found&#39;               # status 0 means found
} else {
  echo &#39;not found&#39;           # status 1 means not found
}</code></pre>
<h2 style="color:MediumVioletRed;">
Ysh returning values from functions
</h2>
<p>Ysh documentation have <a href="https://www.oilshell.org/release/latest/doc/proc-func.html#3-ways-to-return-a-value">documented</a> how to return values from functions. Guess this is also necessary, as there are three ways to return values.</p>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<h3 id="program-arguments-2">Program arguments</h3>
<p>Ysh has an <a href="https://www.oilshell.org/release/latest/doc/ref/chap-builtin-cmd.html#Args-Parser">argument parser</a>. The short option is required, otherwise an error is thrown</p>
<pre class="sh"><code>flag --no-color (&#39;bool&#39;, default=false, help=&#39;&#39;&#39;
   ^~~~
./sample.ysh:18: fatal: proc &#39;flag&#39; wasn&#39;t passed word param &#39;long&#39;</code></pre>
<p>I could not get string arguments to work on options, so that I could supply <code>--list-dir=test</code>. Looking at the ysh source, it appears only to take integer types and bool (unfortunately I cannot link it, as its generated source). Instead I resorted to making the source directory a general argument</p>
<pre class="diff"><code>-./sample.ysh --no-color --list-dir=test
+./sample.ysh --no-color test</code></pre>
<p>Now the above workaround is not without is own problems. Apparently there is no way to set arguments as optional; nor to set a default value. This then have the negative consequence that… you can’t get help</p>
<pre><code>❱ ./sample.ysh -h       
        error &quot;Usage Error: Missing required argument $[arg.name]&quot; (status=2)
        ^~~~~
stdlib/args.ysh:206: fatal: Usage Error: Missing required argument list-dir</code></pre>
<p>Gave up on <code>parseArgs</code>.</p>
<p>Where is the documentation for <code>case</code>? Is <a href="https://www.oilshell.org/release/latest/doc/ref/chap-cmd-lang.html#case">this</a> Osh or Ysh? Looks like bash, so figure Osh. Eventually I only found a couple of examples that still made me have lots of questions</p>
<h3 id="printing-a-dictionary">Printing a dictionary</h3>
<p>How do I print a dictionary? Only found this example</p>
<pre class="ysh"><code>var person = {name: &#39;bob&#39;, age: 42}
json write (person)</code></pre>
<h3 id="value-comparison">Value comparison</h3>
<p>How do I compare (boolean) values?</p>
<pre class="ysh"><code> if (options.help == true) {
                  ^~
./sample.ysh:48: Use === to be exact, or ~== to convert types</code></pre>
<p>Oh, use <a href="https://www.oilshell.org/release/latest/doc/ysh-tour.html#operators"><code>===</code></a> for exact matches.</p>
<pre class="ysh"><code>if (options.help === true) {</code></pre>
<p>Why? <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="calling-a-func-with-no">Calling a <code>func</code> with no <code>()</code></h3>
<p>I did a mistake of calling a function with wrong syntax, but was unaware of it as no complaint was given by the compiler.</p>
<pre class="ysh"><code>func showHelp() {}

call showHelp    # Does nothing?
call showHelp()  # Calls `showHelp`</code></pre>
<p>Its not impossible that both are valid syntax for two different things… but it surprised me negatively</p>
<h3 id="navigating-the-documentation">Navigating the documentation</h3>
<p>I have a real hard time locating stuff in the documentation. The titles are not descriptive enough to me, to be able to figure out where to go to read about things. I mostly landed on the all docs list of content</p>
<figure>
<img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/07/ysh_doc_toc.png" alt="ysh_doc_toc" />
<figcaption aria-hidden="true">ysh_doc_toc</figcaption>
</figure>
<p>Update: found this: <a href="https://www.oilshell.org/release/latest/doc/ref/toc-ysh.html">https://www.oilshell.org/release/latest/doc/ref/toc-ysh.html</a></p>
<h3 id="returning-values-from-proc">Returning values from <code>proc</code></h3>
<p>I keep forgetting that there are two different syntaxes for getting return values. Proc is like bash (Osh?) <code>var files = $(get-files options.list_dir)</code></p>
<h3 id="listing-files-2">Listing files</h3>
<p>I did not find any builtin way to list files, so figured just to use <code>find</code>.</p>
<pre class="ysh"><code>proc get-files (dir) {
    find $dir -type f
}

var files = $(get-files options.list_dir)</code></pre>
<p>That just gives me one long string with newlines. Putting that into an array type just made an array with one (long) item</p>
<pre class="ysh"><code>var file_list = [ files ]</code></pre>
<p>I then attempted to use the <code>readarray/mapfile</code> function</p>
<pre class="ysh"><code>var file_list = []
cat $files | mapfile file_list</code></pre>
<p>That gave me a type if <code>bashArray</code>. Could not figure out what to do with that type. Nothing seemed to accept it?</p>
<p>I had previously found <a href="https://www.oilshell.org/release/latest/doc/ref/chap-builtin-func.html#split"><code>split</code></a>, but was a bit hesitant, as it have a big fat TODO marker. But oh well, it worked <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f604.png" alt="😄" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><code>var file_list = split(files, b'\n')</code></p>
<p>I initially attempted to use <code>find</code> with <code>-print0</code>, but none of the mentions function was happy about that, so settled on newline separation for listed files</p>
<h3 id="print-type">Print type</h3>
<p>Why do printing of type not work, if not stored in a variable?</p>
<pre class="ysh"><code>var d = { &quot;a&quot;: 1, &quot;b&quot;: 2 }

echo type(d) #=&gt; error: Space required before (

var t = type(d)
echo $t  #=&gt; Dict</code></pre>
<h3 id="color-printing-2">Color printing</h3>
<p>Could not find any documentation on color support. Some tickets on github mentions that colors use ansi escape codes. I could not get this to work with <code>echo</code> nor <code>write</code>?</p>
<p>I tried using a <code>func</code> to do <code>printf</code> with ansi colors (just like the bash edition). The colors then work, however I could not get redirection of stdout to a variable to work. This was just be me again confusing capabilities of the two. <code>proc</code> are for <a href="https://www.oilshell.org/release/latest/doc/proc-func.html#procs-vs-funcs">streams</a></p>
<p>Converted to <code>proc</code> (I don’t like that <code>;</code> parameter separation for separating parameter categories). Now I got another problem:</p>
<p>I passed the color function to the actual printing function. As a <code>func</code> I could pass (<code>colorPrint</code>) as an argument just fine &#8211; but not use it at the receiving function. With <code>proc</code> (<code>print-color</code>) I cannot figure out how to pass it as a function pointer. Also tried the various unevaluated expressions, but could not make it work</p>
<pre class="ysh"><code>print-category (^(color-print), options.color, extension, files)</code></pre>
<p>The solution became to be not pass the color function as a function, but instead just call the <code>color-print</code> directly</p>
<pre class="ysh"><code>proc print-category (; option_color, extension, files) {
    color-print (option_color, &quot;Extension: &quot;, blue, extension)</code></pre>
<h3 id="random-number">Random number</h3>
<p>I couldn’t find any random number function, so first thought was that I would just drop in the bash edition. That was of course not the right approach</p>
<pre class="sh"><code>echo $((RANDOM % upper)) # [0; upper-1]
              ^~~~~~
./sample.ysh:118: POSIX shell arithmetic isn&#39;t allowed (parse_sh_arith)</code></pre>
<p>The documentation even states this is not a supported feature</p>
<blockquote>
<p>Shell arithmetic like <code>$(( x + 1 ))</code> and <code>(( y = x ))</code>. Use YSH expressions.</p>
</blockquote>
<p>Also <code>RANDOM</code> is a Bash feature.</p>
<p>Okay, resorting to same solution as used in the Hush solution: use the operating system.</p>
<pre class="ysh"><code>proc get-random-number (; upper) {
    var rand = $( od -vAn -N2 -tu2 &lt; /dev/urandom | tr -d &#39;[:space:]&#39; )
    echo (rand % upper)
}</code></pre>
<p>That didn’t work either</p>
<pre class="error"><code>./sample.ysh:115: &#39;echo&#39; got unexpected typed args
      echo (rand % upper)
      ^~~~
./sample.ysh:115: errexit PID 6347: command.Simple failed with status 2</code></pre>
<p>Putting it in a variable first made it work.</p>
<pre class="diff"><code>proc get-random-number (; upper) {
    var rand = $( od -vAn -N2 -tu2 &lt; /dev/urandom | tr -d &#39;[:space:]&#39; )
-   echo (rand % upper)
+   var value = (rand % upper)
+   echo $value
}</code></pre>
<p>Haven’t found the passage in the documentation that discusses this, but I have now resorted to this intermediate variable workaround (?) several times</p>
<h3 id="returning-truefalse">Returning true/false</h3>
<p>First I forgot that only <code>func</code> functions can use <code>return</code>, not <code>proc</code> functions.</p>
<pre class="ysh"><code>    (else) { return false }
                    ^~~~~
./sample.ysh:138: fatal: &#39;return&#39; expected a small integer, got &#39;false&#39;</code></pre>
<p>Must admit I am getting a little frustrated with the myriad of things that is different between the two concepts… Okay, switching to just <code>true</code> or <code>false</code></p>
<pre class="error"><code>  (else) { false }
           ^~~~~
./sample.ysh:138: errexit PID 17830: command.Simple failed with status 1</code></pre>
<p>Ah yeah. Naturally <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> This was a good opportunity to try out <a href="https://www.oilshell.org/release/latest/doc/proc-func.html#out-params-myvar-is-of-type-valueplace">out parameters</a></p>
<pre class="ysh"><code>proc search-text-in-file (; file, text, out) {
    (else) { call out-&gt;setValue(false) }
}

var has_search_match 
search-text-in-file (search_file, &quot;monzool&quot;, &amp;has_search_match)</code></pre>
<p>That worked great <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f973.png" alt="🥳" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><br/> <br/></p>
<a name="ion-anchor"></a>
<h1 style="color:DodgerBlue;">
Ion
</h1>
<p><a href="https://doc.redox-os.org/ion-manual/">Ion</a></p>
<h2 style="color:MediumVioletRed;">
td;dr
</h2>
<p>Ion shell was not what I hoped for. I think this is definitely shell first, shell scripting second… third… forth. Documentation is a bit like elvis and mutex. At at glance they seem well documented, but in reality reveal only little detail.</p>
<p>The annoyance level was just too high. I stopped early. It is maybe my own fault, and not that of Ion, but it is what it is <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Shell: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
Enjoyment: ☆☆☆☆☆</p>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<h3 id="program-arguments-3">Program arguments</h3>
<p>In the documentation I found no trace of a program argument parsing functionality. What then? A bit weird… there seems to be two matches features? One that <a href="https://doc.redox-os.org/ion-manual/control/03-matches.html">resembles a switch/case</a>, and one that <a href="https://doc.redox-os.org/ion-manual/builtins.html#matches---checks-if-the-second-argument-contains-any-proportion-of-the-first">searches for substrings</a>. None of them appear to support pattern matching</p>
<p>Then there is the string function <a href="https://doc.redox-os.org/ion-manual/expansions/06-stringmethods.html#find"><code>find</code></a></p>
<h3 id="no-result-and-no-syntax-error">No result and no syntax error</h3>
<p>I’m surely blind, but I did struggle a while to understand why <code>replace</code> would not give me the replaced result, instead of just returning input unmodified… Then I saw it</p>
<pre class="diff"><code>-list_dir = $replace($arg, &quot;--list-dir=&quot;, &quot;&quot;)
+list_dir = $replace($arg &quot;--list-dir=&quot; &quot;&quot;)</code></pre>
<p>Gave no error, but no expected operation either.</p>
<h3 id="modifying-variables">Modifying variables</h3>
<p>This distilled piece of code is invalid</p>
<pre class="ion"><code>let list_dir = &quot;.&quot;
for arg in @args
    if matches $arg &quot;--list-dir&quot;
        list_dir = $replace($arg, &quot;--list-dir=&quot;, &quot;&quot;)
    end
end</code></pre>
<p>It gives this error</p>
<pre class="error"><code>ion: pipeline execution error: command not found: list_dir</code></pre>
<p>I wish this error was more precise on <strong>where</strong> the error is… And what is the compiler complaining about? Are variables immutable? I found no mention of this in the documentation <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Then I tried this</p>
<pre class="diff"><code>-list_dir = $replace($arg, &quot;--list-dir=&quot;, &quot;&quot;)
+$list_dir = $replace($arg, &quot;--list-dir=&quot;, &quot;&quot;)</code></pre>
<p>This was greeted by this error:</p>
<pre class="error"><code>ion: pipeline execution error: command exec error: Permission denied (os error 13)</code></pre>
<p>Clearly not the solution.</p>
<p>Actually I fail to find a single example in the documentation, where a variable is re-assigned to a value. Everything is just echo’ed.</p>
<p>Update: finally found an example. The <a href="https://doc.redox-os.org/ion-manual/control/02-loops.html#while-loops">while loop</a> show how to update a variable</p>
<pre class="ion"><code>let value = 0
while test $value -lt 6
    echo $value
    let value += 1
end</code></pre>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f610.png" alt="😐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="returning-values-from-functions">Returning values from functions</h3>
<p>I could not at all find any example, on how to return values from a function. I first tried <code>return</code></p>
<pre class="ion"><code>fn sample arg
    let value = $arg
    return $value
end

let value = sample &quot;ion&quot;</code></pre>
<p>This error’ed on something that perhaps pointed mostly to the call site?</p>
<pre class="error"><code>ion: assignment error: extra values were supplied, and thus ignored. Previous assignment: &#39;value&#39; = &#39;sample&#39;
ion: expansion error: Variable &quot;value&quot; does not exist</code></pre>
<p>I am left for guessing here.</p>
<pre class="diff"><code>-let value = sample &quot;ion&quot;
+let value = $(sample &quot;ion&quot;)</code></pre>
<p>This gave no error &#8211; but no result either?</p>
<p>Success!</p>
<pre class="ion"><code>fn sample arg
    let value = $arg
    echo $value
end

let value = $(sample &quot;ion&quot;)
echo $value</code></pre>
<p>In hindsight probably not unexpected; just not what I was hoping for</p>
<h3 id="hashmap-errors">Hashmap errors</h3>
<p>Hashmap appears to take only the same type, but then everything also seems to be a string</p>
<pre class="ion"><code>fn setup_options options args
    echo @options
end

let options_defaults:hmap[str] = [ list_dir=&quot;test&quot; no_color=false ]
let options = setup_options @option_defaults @args
for key value in @options
    echo $key: $value
end</code></pre>
<pre class="error"><code>ion: assignment error: extra values were supplied, and thus ignored. Previous assignment: &#39;options&#39; = &#39;setup_options&#39;
ion: expansion error: Variable &quot;options&quot; does not exist</code></pre>
<h3 id="error-messages-1">Error messages</h3>
<p>Error messages are a quite sparse. There is no indication of which line failed, and given more complex code it becomes very difficult to figure out where the actual mistake is</p>
<pre class="sh"><code>ion: expansion error: Variable &quot;arg,&quot; does not exist</code></pre>
<p><br/> <br/></p>
<a name="clojure-anchor"></a> <a name="babashka-anchor"></a>
<h1 style="color:DodgerBlue;">
Babashka
</h1>
<p><a href="https://babashka.org/">babashka</a></p>
<p>Babashka is super interesting. The sole purpose of babashka is to make Clojure a feasible option for scripting. Its a well made clojure implementation that features super fast start up times due to its use of <a href="https://www.graalvm.org/">GraalVM</a></p>
<p>Here are some relevant links:</p>
<ul>
<li>Good introduction: <a href="https://www.braveclojure.com/quests/babooka/">https://www.braveclojure.com/quests/babooka/</a></li>
<li>Running shell commands: <a href="https://github.com/babashka/process">process</a></li>
<li>Babashka as a bash alternative: <a href="https://presumably.de/how-to-do-things-with-babashka.html">how-to-do-things-with-babashka</a></li>
<li>Easier packaging of single file executable using <a href="https://github.com/nikvdp/bbb">bbb</a></li>
<li>File handling library: <a href="https://github.com/babashka/fs">fs</a></li>
</ul>
<h3 id="embedded-system-application">Embedded system application</h3>
<p>I’m a bit unclear on what requirement there actually are to get babashka cross-compiled to a system that would not fit Java nor GraalVM.</p>
<p>There does not seem to be much information on cross-compiling Java itself. I got only a few hits from Google on the matter. Openjdk had an <a href="https://openjdk.org/groups/build/doc/building.html">article</a>, but otherwise it seems not to be a common thing to do. Then again, I am not sure if that is even a requirement. Babashka uses [GraalVM] (https://www.graalvm.org/) to compile itself to native. Perhaps the challenge is to get GrallVM to cross-compile. It certainly can do <a href="https://blogs.oracle.com/developers/post/building-cross-platform-native-images-with-graalvm">cross-platform</a> compilation, so the same program can be compiled to Linux, MacOS and Windows. The situation is a bit more muddy when it comes to cross architecture support. It looks like GraalVM do not have official <a href="https://www.graalvm.org/latest/reference-manual/native-image/metadata/Compatibility/">arm32</a> support. There are some mentions of arm32 working anyway. Given the lack of official documentation, I suspect I would be in for a world of hurt, if attempting to target a tiny embedded system.</p>
<p>I think it’s a safe assumption, that if I go with babashka, I keep on bash’ing on my embedded platforms. I would also add, that I have the feeling that babashka is made more for <em>scripting</em>, than for <em>shell scripting</em> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><br/> <br/></p>
<a name="planck-anchor"></a>
<h1 style="color:DodgerBlue;">
Planck
</h1>
<p><a href="https://planck-repl.org/">Planck</a></p>
<p>Planck is actually a <strong>ClojureScript</strong>. It is implemented in C and utilizes the JavaScriptCore. Planck can consume jar files for extra features, but provide its own library also. Its standard library supports the <a href="https://cljdoc.org/d/planck/planck/2.27.0/doc/planck-namespaces#planckshell">shell</a> commands of Clojure</p>
<p>Its front page has a perfect example</p>
<pre class="clojure"><code>(require &#39;[planck.core :refer [line-seq with-open]]
         &#39;[planck.io :as io]
         &#39;[planck.shell :as shell])

(with-open [rdr (io/reader &quot;input.txt&quot;)]
  (doseq [line (line-seq rdr)]
    (println (count line))))

(shell/sh &quot;say&quot; &quot;done&quot;)</code></pre>
<p>The fact that it is written in C, possibly makes it very portable. However being a ClojureScript variant, it builds on top of what I expect is a rather large dependency &#8211; the JavaScriptCore library. I could not find any size requirements for JavaScriptCore, but <a href="https://www.seanmcp.com/articles/quick-comparison-of-javascript-and-go-executables/">Sean McPherson</a> mentions that the <a href="https://bun.sh/">Bun</a> runtime (which also builds upon JavaScriptCore) weights in at about 98 MB. That is sadly way to much for a small embedded system</p>
<p><br/> <br/></p>
<a name="joker-anchor"></a>
<h1 style="color:DodgerBlue;">
Joker
</h1>
<p><a href="https://joker-lang.org/">Joker</a></p>
<p>Joker is not as such meant for shell scripting, but it has a shell function <a href="https://candid82.github.io/joker/joker.os.html#sh">sh</a> (and variants) that make it possible to run external programs. Joker is written in Go and thus should be pretty portable. Being a Go project, I had hoped it would have small space requirements. Unpacking the prebuilt packages show a roughly 26 MB binary. That is sadly still to much for a tiny embedded system</p>
<p><br/> <br/></p>
<a name="fennel-anchor"></a>
<h1 style="color:DodgerBlue;">
Fennel
</h1>
<p><a href="https://fennel-lang.org/">Fennel</a></p>
<p>It does not appear that Fennel have any additions that enables calling external processes. This makes sense as it sits on top of Lua, and Lua’s built in capabilities for running and managing external programs are pretty poor. Erik Sank have a nice <a href="https://github.com/eriksank/localexec#5-similar-modules-and-functions">summary</a> of the downsides. Erik Sank also made a library <a href="https://github.com/eriksank/localexec">localexec</a> to enhance Lua’s handling of external programs. I can see the implementation uses temporary files in <em>/tmp</em>, ipc over shared memory files (<em>/dev/shm</em>) and other tricks. In essence… it is complicated to retrofit this feature to Lua in Lua</p>
<p>In my previous <a href="https://monzool.net/blog/2017/07/04/a-search-for-bash-scripting-alternatives/#luash">search</a> for a bash alternative, I mentioned zserge’s <a href="https://github.com/zserge/luash">luash</a> . Btw, I recommend to read his <a href="https://zserge.com/posts/luash/">post</a> about luash, where he also links to Edgar Toernig description of why <a href="http://lua-users.org/lists/lua-l/2007-10/msg00189.html">popen is complicated</a>. Already then, I also mentioned that luash appeared to be <a href="#luash-2017">abandonware</a>. Last change was in 2015. But! I have discovered Johannes Blaschkes <a href="https://github.com/JBlaschke/luash">fork</a> that at time of writing is 46 commits ahead of the original. Anyway, the luash implementation also messes with files in <em>/tmp</em> to make things work. It’s complicated when the underlying engine do not have the facilities…</p>
<p>I am not confident Lua is a fitting platform for doing heavy lifting in calling external programs</p>
<p><br/> <br/></p>
<a name="janet-anchor"></a>
<h1 style="color:DodgerBlue;">
Janet
</h1>
<p><a href="https://janet-lang.org/">Janet</a></p>
<h2 style="color:MediumVioletRed;">
tl;dr
</h2>
<p>The fact that I am not familiar with lisp was certainly a downside, but in general I think it was easy to adapt to. A downside to the experience was, that the documentation is lacking. There are definitely some great and elaborative articles in the documentation, especially the most used data types are very well described. The situation get a bit more lacking when venturing into its large library of functions. Some features have been documented by the community (which is great), but most function just have a short summary. There are also a few function that do not even have a summary. I managed to get by with a mix of the Janet documentation, the Clojure documentation, the Janet ticket system and some trial and error.</p>
<p>Despite my struggles, I actually had great fun. To my surprise, the end result also have lot of similarity with the original bash sample. I realize this is because I probably don’t lisp like a lisper. Anyway the end result reads pretty easy from a bash mindset. Writing script in lisp is perhaps not as far fetched an approach as I original thought.</p>
<p>Janet have been a great experience!</p>
<p>vscode language plugin: https://marketplace.visualstudio.com/items?itemName=janet-lang.vscode-janet, https://marketplace.visualstudio.com/items?itemName=CalebFiggers.vscode-janet-plus-plus vscode formatter plugin: https://marketplace.visualstudio.com/items?itemName=dlyanb.janet-formatter</p>
<p>Repl: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
Approachable for a bash’er: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆<br />
Enjoyment: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>
<h2 style="color:MediumVioletRed;">
About Janet
</h2>
<p>Janet is a lisp by Calvin Rose, and have this nice catchphrase:</p>
<p>“<strong>Janet</strong> is a programming language for system scripting, expressive automation, and extending programs written in C or C++ with user scripting capabilities.”</p>
<p>Coincidentally Calvin Rose is also the original creator of <a href="#fennel-anchor">Fennel</a>.</p>
<p>Janet have many advanced features, and is implemented in portable C. The website show an example of compiling to 32 Haiku, and on my blog have a small guide on how to <a href="https://monzool.net/blog/2019/12/13/cross-compiling-janet-lang/">cross-compile Janet to armv5</a>. I would have stamped this as “perfekt <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" />”, but it appears that since my cross-compile exercise, janet now requires a <a href="https://github.com/janet-lang/janet/pull/1133">boot step</a> to be run on the destined host. That kind of bootstrapping always complicates the cross-compiling machinery <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1fae4.png" alt="🫤" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Andrew Chambers has two projects to make Janet have better shell capabilities &#8211; <a href="https://github.com/andrewchambers/janetsh">janetsh</a> &#8211; a systems shell in Janet &#8211; <a href="https://github.com/andrewchambers/janet-sh">janet-sh</a> &#8211; a library to make it easy to call external programs from Janet</p>
<p>janetsh appear abandonware, but its janet-sh that is of interest anyway.</p>
<p>A hesitation about janet is that these one-man lisp or scheme implementations often die off with time (probably for life and reasons). Janet however, is very alive and kicking</p>
<pre class="sh"><code>❱ git log --date=format:&#39;%Y&#39; --pretty=format:&#39;%ad&#39; | sort | uniq -c | awk &#39;{print $2 &quot;: &quot; $1}&#39;
2017: 178
2018: 550
2019: 978
2020: 928
2021: 640
2022: 357
2023: 518
2024: 110</code></pre>
<p>The community appear to be a big part of janet. There is a community driven library <a href="https://janet-lang.org/api/spork/index.html">spork</a> hosted on the official webpage. Also many documentation examples are provided by the community. Spork can be installed in one big bulk with <code>jpm install spork</code> &#8211; or one can use jpm to install the packages individually.</p>
<p>For the janet sample, I chose to allow the use of the external libraries., something that I otherwise refrained from using. I think the Janet situation is a bit different from e.g bash</p>
<ul>
<li>It is supported with an official package manager (jpm)</li>
<li>The documentation is part of the official documentation</li>
<li>Packages can be included individually or as a spork library</li>
<li>I am already using <code>janet-sh</code> to get shell capabilities</li>
</ul>
<p>In fact, the Janet package system reminds pretty much of that of mruby..</p>
<p>Additionally for documentation, there is a free book <a href="https://janet.guide/">Janet for Mortals</a> for learning Janet. It has a great <a href="https://janet.guide/scripting/">chapter</a> on shell scripting with the sh module</p>
<h2 style="color:MediumVioletRed;">
Installation
</h2>
<p>Install from source is trivial. Following the install guide will also install the package tool <a href="https://janet-lang.org/docs/jpm.html">jpm</a></p>
<p>Then to install the janet-sh extension:</p>
<pre><code>jpm install https://github.com/andrewchambers/janet-sh.git</code></pre>
<p>The entire output directory i 5.4 MiB including man-pages and Janet as a binary, static library and dynamic library. The Janet binary and <em>sh</em> library is 825 KiB</p>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
Calling external programs
</h2>
<p>Janet have several alternatives to make Janet call external programs In descending abstraction level</p>
<h3 id="janet-sh">janet-sh</h3>
<p>The contributor library <a href="https://github.com/andrewchambers/janet-sh">janet-sh</a> gives janet capabilities to operate very much like a shell scripting language. janet-sh adapts an api that bridges unmanaged shell commands with strict Janet types. The means that, for example, files referenced in a command must be given as real Janet file handles</p>
<pre class="clojure"><code>(with [f (file/open &quot;monzool.txt&quot;)]
  (sh/$ echo &quot;monzool&quot; &gt; ,f))</code></pre>
<p>Redirecting stdin and stdout streams are interfaced with e.g. the <a href="https://janet-lang.org/docs/syntax.html">buffer</a> type.</p>
<pre class="clojure"><code>(def output @&quot;&quot;)
(sh/run echo &quot;Hello shell&quot; &gt; ,output)</code></pre>
<h3 id="osshell"><code>os/shell</code></h3>
<p>The janet <a href="https://janetdocs.com/os/shell">os/shell</a> function give a non-integrating experience that is more traditional from non-shell languages.</p>
<p>A string is given to <code>os/shell</code> and handed verbatim to the standard shell</p>
<pre class="clojure"><code>(os/shell &quot;ls &gt; listing.txt&quot;)
(os/shell &quot;top&quot;)</code></pre>
<h3 id="osspawn"><code>os/spawn</code></h3>
<p>The <a href="https://janet-lang.org/docs/process_management/spawn.html">os/spawn</a> command utilizes no shell, but executes a process directly on the system. This is a low level function, and perhaps not normally relevant in shell scripting contexts &#8211; but can be useful in some situations</p>
<h3 id="smoke-test">Smoke test</h3>
<p>Just for good manners, and to smoke test the installation, I tested out the janet-sh <a href="https://github.com/andrewchambers/janet-sh?tab=readme-ov-file#quick-examples">frontpage example</a>…</p>
<pre class="clojure"><code>(import sh)

# raise an error on failure.
(sh/$ touch foo.txt)

# raise an error on failure, return command output.
(sh/$&lt; echo &quot;hello world!&quot;)
&quot;hello world!\n&quot;

# return true or false depending on process success.
(when (sh/$? true)
  (print &quot;cool!&quot;))

# pipelines
(sh/$ cat ,path | sort | uniq)

# pipeline matching
(match (sh/run yes | head -n5)
  [0 0] :ok)</code></pre>
<pre><code>cool!
./janet-sh_example.janet:15:1: compile error: unknown symbol path</code></pre>
<p>Ah shoot. A runtime error on <code>path</code>. I have infinitesimal lisp/clojure experience, but I managed to make the example run with few modifications <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f605.png" alt="😅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="diff"><code>-(sh/$&lt; echo &quot;hello world!&quot;)
-&quot;hello world!\n&quot;
+(def result (sh/$&lt; echo &quot;hello world!&quot;))
+(print result)

+(def path &quot;foo.txt&quot;)
(sh/$ cat ,path | sort | uniq)</code></pre>
<p>This worked fine. I just needed to put some data in <em>foo.txt</em> to make the example actually do all it was meant to. Then it got strange. I added this</p>
<pre class="clojure"><code>(sh/$ echo &quot;Janet&quot; &gt; ,path)</code></pre>
<p>and got a stack trace in the terminal</p>
<pre class="error"><code>error: unsupported redirect :&gt; :string
  in thunk [./janet-sh_example.janet] (tail call) on line 16, column 1</code></pre>
<p>How do I make it accept anything with <code>&gt;</code>?</p>
<p>I found an example from the janet-sh <a href="https://github.com/andrewchambers/janet-sh/blob/master/test/sh.janet">test suite</a></p>
<pre class="clojure"><code>(sh/$ echo hello &gt; ,out-buf)</code></pre>
<p>Why do my addition not work, if this works? <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> This made me a little suspicious, so I tried running the test suite</p>
<pre class="error"><code>error: assert failure in (deep= @&quot;cba&quot; out-buf)
  in thunk [./test.janet] (tail call) on line 29, column 1</code></pre>
<p>I commented out as much as possible to narrow it down, and the test ran without complaining. Ohhh, its using a <a href="https://janet-lang.org/docs/syntax.html">buffer</a>!</p>
<pre class="clojure"><code>(def out-buf @&quot;&quot;)
(sh/$ echo hello &gt; ,out-buf)
(assert (deep= out-buf @&quot;hello\n&quot;))</code></pre>
<p>I found a <a href="https://acha.ninja/blog/dsl_for_shell_scripting/">blog post</a> on janet-sh by Andrew Chambers. That also had a redirect to buffer example. That made me reflect at bit. <code>unsupported redirect :&gt; :string</code> &#8211; okay Einstein, the compiler is telling you that a string won’t work. A buffer works, so I figured a file would probably also work <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f91e.png" alt="🤞" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="clojure"><code>(def f (file/open &quot;foo.txt&quot; :w))
(sh/$ echo &quot;monzool&quot; &gt; ,f)
(file/close f)</code></pre>
<p>Success <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Okay, I am now educated from the school of Janet <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f604.png" alt="😄" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
Sample
</h2>
<h3 id="printing">Printing</h3>
<p>Print functions seem kind of oddly named. <a href="https://janet-lang.org/api/misc.html#prin"><code>prin</code></a> for print <strong>without</strong> newline and <a href="https://janet-lang.org/api/misc.html#print"><code>print</code></a> for printing <strong>with</strong> newline… whats wrong with <code>print</code> and <code>println</code>?</p>
<h3 id="get-list-of-files-1">Get list of files</h3>
<p>There is <a href="https://janetdocs.com/os/dir"><code>os/dir</code></a>, but that lists files and directories in one directory level only. No option to list sub-directories or to filter in only files.</p>
<p>Oh no, I get the impression that I manually have to roll out a solution for listing. It have the building blocks I need: <code>os/dir</code>, <code>os/stat</code>, <code>os/cd</code> and then probably apply <code>each</code> or <code>loop</code> in a recursive manner.</p>
<p>I found a package <a href="https://github.com/jeannekamikaze/janet-filesystem">janet-filesystem</a>, which do have a function <code>filesystem/list-all-files</code>. It doesn’t document how that data is presented. And then I have to bring in yet another package.</p>
<p>Okay, good that I have the janet-sh package. Classic <code>find</code> it is then.</p>
<p>Btw. Wonder if this is just the printing that goes wrong, but the utf-8 filenames do not pretty print when using the os functions. The shell edition prints fine <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="clojure"><code>(os/dir &quot;test&quot;)
@[&quot;\xF0\x9F\x84\xBC\xF0\x9F\x84\xBE\xF0\x9F\x84\xBD\xF0\x9F\x85\x89\xF0\x9F\x84\xBE\xF0\x9F\x84\xBE\xF0\x9F\x84\xBB.net&quot;

(sh/$ find test -type f)
test/🄼🄾🄽🅉🄾🄾🄻.net</code></pre>
<h3 id="main">main</h3>
<p>Like the other language samples, I had defined a <code>main</code> and tried to call it, but I could not figure out have to call it</p>
<pre><code>compile error: &lt;function main&gt; expects at least 3 arguments, got 0</code></pre>
<p>When reading https://janet.guide/all/#compilation-and-imagination I found out, that its a built in function. It is called <strong>automatically</strong> if defined <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="program-arguments-4">Program arguments</h3>
<p>There is a community library <a href="https://github.com/janet-lang/argparse">argparse</a> for parsing arguments, that works very well. It even generates the help option itself.</p>
<h3 id="sporkpath">spork/path</h3>
<p>The spork library have a <a href="https://janet-lang.org/api/spork/path.html">path</a> library for various path manipulations. I had intended to use <code>path/ext</code>. It has the description <em>“Get the file extension for a path.”</em>, but the result is wrong</p>
<pre class="clojure"><code>(spork/path/ext &quot;file.ext&quot;)
&quot;.ext&quot;</code></pre>
<p>I would argue that <em>“ext”</em> is the extension here, and <em>“.”</em> is the extension <strong><em>separator</em></strong>. I resorted to use <code>string/split</code> instead.</p>
<p>The function to get the path delimiter also returns unexpected values</p>
<pre class="clojure"><code>(print spork/path/delim)
:</code></pre>
<p>If operating on <code>"${PATH}"</code> this would be a correct answer, but this library works on file paths and should have given <code>/</code> in my case.</p>
<p>I ended up dropping spork/path.</p>
<h3 id="filename-splitting">Filename splitting</h3>
<p>I spent a bit of time with the <a href="https://janet-lang.org/api/misc.html#-%3E">threading form</a> to do splitting. Common knowledge I supposed, but fun enough to play around with <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f604.png" alt="😄" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<pre class="clojure"><code>repl:1:&gt; (string/split &quot;.&quot; &quot;file.ext&quot;)
@[&quot;file&quot; &quot;ext&quot;]

repl:2:&gt; (-&gt; &quot;.&quot; (string/split &quot;file.ext&quot;))
@[&quot;file&quot; &quot;ext&quot;]

repl:3:&gt; (-&gt;&gt; &quot;file.ext&quot; (string/split &quot;.&quot;))
@[&quot;file&quot; &quot;ext&quot;]</code></pre>
<h3 id="checking-table-for-key">Checking table for key</h3>
<p>It took a bit to figure out how to check if a key is present in a table. Neither the table <a href="https://janet-lang.org/docs/data_structures/tables.html">introduction</a> nor the <a href="https://janet-lang.org/api/table.html">api</a> explained this. I expect this would be general lisp knowledge, so I guess I cannot blame janet for this. Searching the <a href="https://janet-lang.org/api/index.htm">index</a> for the word “key”, let me to <a href="https://janet-lang.org/api/index.htm">has-key?</a>. The documentation index saved me many times <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f604.png" alt="😄" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="random-numbers">Random numbers</h3>
<p>My random function always return 3?</p>
<pre class="clojure"><code>(defn get-random-number [max]
  (-&gt;&gt; (math/random)
       (* max)
       (math/floor)))

(let [random-number (get-random-number (length file-list))
#=&gt; 3</code></pre>
<p>I then tried <a href="https://janet-lang.org/api/math.html#math/rng-int">math/rng-int</a></p>
<pre class="clojure"><code>repl:113:&gt; (math/rng-int (math/rng) 8)
2
repl:114:&gt; (math/rng-int (math/rng) 8)
2
repl:115:&gt; (math/rng-int (math/rng) 8)
2</code></pre>
<p>Surely a seed is needed. There was no community example on how to use the random functions, so I found a <a href="https://github.com/janet-lang/janet/blob/master/test/suite-math.janet">unit-test</a> to model from</p>
<pre class="clojure"><code>(defn get-random-number [max]
  (let [seed (math/rng &quot;monzool&quot;)
        engine (math/rng (:int seed))]
    (math/rng-int engine (- max 1))))

repl:162:&gt; (get-random-number 8)
4
repl:163:&gt; (get-random-number 8)
4
repl:164:&gt; (get-random-number 8)
4</code></pre>
<p>Okay, this is probably a global seed function then</p>
<pre class="clojure"><code>(defn get-random-number [max]
  (let [seed (math/rng &quot;monzool&quot;)
        engine (math/rng (:int seed))]
    (math/rng-int engine (- max 1))))

repl:169:&gt; (get-random-number 8)
4
repl:170:&gt; (get-random-number 8)
5
repl:171:&gt; (get-random-number 8)
2</code></pre>
<p>Yeah… but that won’t help me one bit, as I will only be running the script once, and then always get the same “random” number (reminds me of that Dilbert random number sketch).</p>
<p>Ended up using <em>/dev/urandom</em>, but surely there must be a way to get a random random number in Janet <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f914.png" alt="🤔" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="janet-sh-1">janet-sh</h3>
<p>I struggled a bit with getting some shell expressions to work with janet-sh</p>
<p>To get a random number from the operating system, I reused a command expression to read from <code>/dev/urandom</code></p>
<pre class="clojure"><code>repl:3:&gt; (sh/run od -vAn -N2 -tu2 &lt; /dev/urandom | tr -d &#39;[:space:]&#39;)
repl:3:60: parse error: mismatched delimiter ),  opened at line 15, column 59</code></pre>
<p>It was less that obvious to me, what the error was. I simplified the command, just to get different error</p>
<pre class="clojure"><code>repl:4:&gt; (sh/$ od -vAn -N2 -tu2 &lt; /dev/urandom)
repl:4: error: :dup2 value must be a file, got /dev/urandom</code></pre>
<p>That was easy solvable</p>
<pre class="clojure"><code>repl:5:&gt; (def f (file/open &quot;/dev/random&quot; :r))
repl:6:&gt; (sh/$ od -vAn -N2 -tu2 &lt; ,f)</code></pre>
<p>That worked. By elimination, that would mean that the <code>tr</code> command was the issue. I studied the <a href="https://janet.guide/scripting/">Janet for Mortals</a> a bit deeper, and found this great hint</p>
<p>“<em>Janet’s <code>backtick</code> -quoted strings are a really nice way to sidestep shell quoting problems.</em>”</p>
<pre class="clojure"><code>repl:14:&gt; (sh/run od -vAn -N2 -tu2 &lt; ,f | tr -d `[:space:]`)
33720</code></pre>
<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3c6.png" alt="🏆" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h3 id="string-to-int">String to int</h3>
<p>Captured stdout from the shell arrives in a buffer. This I attempted to convert to an integer. The functions I could find that looked like useful for the conversion was <code>int/u64</code>. Before this I had to convert the buffer to a string. I ended up with this</p>
<pre class="clojure"><code>(def output @&quot;&quot;)
(sh/run od -vAn -N2 -tu2 &lt; ,f | tr -d `[:space:]` &gt; ,output)
(let [random-number (-&gt;&gt; output
                        (string/split &quot;\0&quot;)
                        (first)
                        (int/u64))]</code></pre>
<p>Then I found a cook-book <a href="https://github.com/MikeBeller/janet-cookbook">PR</a> which showed some better tools.</p>
<pre class="diff"><code>-(let [random-number (-&gt;&gt; output
-                        (string/split &quot;\0&quot;)
-                        (first)
-                        (int/u64))]
-  (% random-number max)
+(-&gt; output
+  (scan-number)
+  (% max))))</code></pre>
<h3 id="color-printing-3">Color printing</h3>
<p>I found no dedicated color printing function, but the jpm program (the janet package manager) do have a helper module <a href="https://janet-lang.org/api/jpm/shutil.html"><code>shutil</code></a> that provides a <code>color</code> function. Sadly, as many other modules in janet, <code>shutil</code> is largely undocumented, and I could not get it to generate working color commands</p>
<pre class="clojure"><code>repl:41:&gt; (import jpm/shutil)

repl:42:&gt; (shutil/color :blue &quot;monzool&quot;)
&quot;\e[0mmonzool\e[0m&quot;
repl:43:&gt; (shutil/color blue &quot;monzool&quot;)
repl:43:1: compile error: unknown symbol blue
repl:44:&gt; (shutil/color 34 &quot;monzool&quot;)
&quot;\e[0mmonzool\e[0m&quot;
repl:45:&gt; (shutil/color &quot;\e[33m&quot; &quot;monzool&quot;)
&quot;\e[0mmonzool\e[0m&quot;</code></pre>
<hr />
<p><br/> <br/></p>
<a name="epiloge-anchor"></a>
<h1 style="color:DodgerBlue;">
Epiloge
</h1>
<p>Please note that this is based on my own experience. You might have a completely different experience. My expectations might also have been off for some of the projects. Take this as a subjective evaluation that do not necessarily apply to others. It could very well be that I am just an idiot, and have understood nothing <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f921.png" alt="🤡" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>I think the languages that was most like a mental <em>Jean Claude Van Damme round house kick to the head</em>, was nushell and janet. The bell got rung, and I learned a lot in the process <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f468-200d-1f393.png" alt="👨‍🎓" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>For the shells, the stdin/stdout is a fundamental way of transporting data between functions. This makes a lot of sense on the command line, especially as structured data that can be manipulated with great flexibility. From a strictly scripting perspective, I think this approach has an inherit downside: the stdout is occupied for data exchange and is not available for printing to the user. I think these next generation shells should have taken the opportunity to separate information to the user/developer from the data for the commands <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f500.png" alt="🔀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h2 style="color:MediumVioletRed;">
Enjoyment
</h2>
<p>How enjoyable was the experience. This is a combination of how good the documentation is and the “ergonomics” of the language. Enjoyment does not correlate with how much the language is similar to Bash, and thus how familiar the experience was, but more if the experience was fun.</p>
<p>bash: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆<br />
elvis: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
murex: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
hush: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆<br />
koi: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆<br />
abs: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆<br />
nushell: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆<br />
ysh: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
ion: ☆☆☆☆☆<br />
janet: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆</p>
<h2 style="color:MediumVioletRed;">
Ease of adoption
</h2>
<p>If you know bash, how easy is it to pick up the language</p>
<p>bash: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆ <sup>(no one really knows bash)</sup><br />
elvis: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
murex: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
hush: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br />
koi: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆<br />
abs: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆<br />
nushell: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
ion: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆☆<br />
ysh: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆☆<br />
janet: <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />☆☆</p>
<h2 style="color:MediumVioletRed;">
Production ready
</h2>
<p>In the end, how much do you trust including the language in a commercial product? Is it field tested? How is the test coverage? How is the general quality? Is there a chance anyone will answer the questions you have, or fix that bug you found?</p>
<p>What if to be used in medical industry equipment, or other high reliability demanding domains &#8211; what would you trust then?</p>
<p>What is the longevity of the language? If the project just dies out and becomes abandonware, then no one will praise you for selecting that language. It might not be a real problem, but if a problem arise, then now you are on your own and have to decide if your client/boss/timeline allows you to maintain <strong>your product</strong> code <strong>and</strong> a scripting language.</p>
<p>Good documentation I consider a must for a language that is to be used in a team environment. Chances are that several people are going to maintain the scripts, and then learning material is essential, as not everybody has the desire invest the effort and time to learn some “arbitrary” chosen language.</p>
<p>As the word goes <em>“It is tough to make predictions, especially about the future.”</em> and from my limited knowledge of these projects, answering these questions would guesswork and gut feelings… But <strong>if</strong> venturing into that (guesswork and gut feelings), I would think that babashka and nushell are currently best bets on a languages to fulfil those requirements, with ysh approaching confidently. I do not know anything about elvish or murex, but for sure they both have a long pedigree of continued development</p>
<hr />
<p><br/> <br/></p>
<a name="conclusion-anchor"></a>
<h1 style="color:DodgerBlue;">
Conclusion
</h1>
<p>I am really happy about the decision to make this an exercise of actually trying out the different languages, instead of just theorising about the properties and capabilities of a language. It is also the only way to really get the authentic feeling of a language, and a proper evaluation of its documentation.</p>
<p>The contestants grouped into widely different domains. Some modernizes the shell concept (perhaps inspired by PowerShell), while others draw on the heritage of lisps with a more kitchen-sink mindset; and then some have pursued the philosophy of making a simple script language that provide easy access to external tools.</p>
<p>In hindsight I think I did not accept up front, that the next-gen shells (elvish, murex, nushell) are primarily shells that occupy stdin/stdout for streaming structured data, and kind of leaves the use-case for scripting as a secondary tier. I don’t know if this is a fair assessment, but that was how I perceived the experience. While nushell was an eye-opening experience, then perhaps one day the <a href="https://en.wikipedia.org/wiki/Rosy_retrospection">rosy retrospection</a> will kick in and I will revisit elvish and murex, but I believe from the contenders tried in this round, a new shell is not what I am looking for.</p>
<p>I was perhaps most excited about Janet, but that cooled a bit when I discovered it requires to run on the host for cross-compiling. It is not an impossible task, but that kind of bootstrapping always complicates the cross-compiling machinery. Then there is the giant elephant in the room, if I can persuade team members to use a lisp <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4c9.png" alt="📉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>Where I think ysh have taken effort to handle every conceivable special case and scenario, it has also gotten complicated. With the opposite philosophy are hush, koi and abs, which was all very good, but also very simple. They provide the structural skeleton, control flow and data types with perhaps a few helper functionalities &#8211; and that’s about it. This might be good enough though, and make a sufficient compromise? Unfortunately it appears that both koi and abs is now abandonware, and hush is on the verge of becoming it <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26b0.png" alt="⚰" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Being abandonware doesn’t mean that they are not useful, but facts are that they do not feel 100% complete as is, and without active development that will never happen <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f61f.png" alt="😟" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>In conclusion, I learned a lot &#8211; but I am not sure I actually came closer to finding a true bash alternative…</p>
<figure>
<img decoding="async" src="https://monzool.net/blog/wp-content/uploads/2024/07/kermit_window.jpg" alt="kermit_window" />
<figcaption aria-hidden="true">kermit_window</figcaption>
</figure>
<p>Perhaps it is time to accept that small embedded systems are stuck on bash, and future midsize to large embedded systems approach a size where e.g. python is a viable route. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f40d.png" alt="🐍" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<hr />
<p><br/> <br/></p>
<a name="addendum-anchor"></a>
<h1 style="color:DodgerBlue;">
Addendum
</h1>
<a name="addendum-future-candidates-anchor"></a>
<h1 style="color:DodgerBlue;">
Future candidates
</h1>
<h2 style="color:MediumVioletRed;">
Rash
</h2>
<p><a href="https://rash-lang.org/">Rash</a></p>
<p>Rash is a shell that is build on top of <a href="https://racket-lang.org/">Racket</a>. Rash can also be used for scripting, and provides a nice terse language extension to Racket that do not require the famous parentheses of scheme and lisp. Rash have many interesting ideas on how to intermixing shell commands and racket source I have not yet looked into Racket for size requirements. I hear some people references Racket as the scheme equivalent of python. Cross-compiling to arm32 appears to be supported.</p>
<p>See this interesting presentation for features of Rash: https://www.youtube.com/watch?v=Acjqx1MPkw4</p>
<p>The main concern is that is appears to be gravitating towards becoming abandonware</p>
<h2 style="color:MediumVioletRed;">
Python
</h2>
<p>Maybe it is time to accept that python will assimilate us all, and resistance is futile <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f3f3.png" alt="🏳" class="wp-smiley" style="height: 1em; max-height: 1em;" />. It used to be a bit complicated to put on an embedded system, but times have changed and as using systemd is not given much second thought, then lots of desktop systems now use python and that trend is sieving into the embedded marked also. Personally I have done a lot of python stuff on the desktop, but I do not find it particularly exciting. I also find python a horrible language for doing shell scripting work in, but there are some projects that lift the experience to something worth considering:</p>
<h3 id="xonsh">xonsh</h3>
<p><a href="https://xon.sh/">xonsh</a></p>
<p>Xonsh is a shell first project that mixes bash like syntax with python. Xonsh has been in active development for a long time, yet at time of writing the current version is 0.17.0, with no indication of a version 1.0 in the horizon. An interesting side-note is that different language in the python ecosystem can tap into xonsh. Coconut has such <a href="https://coconut.readthedocs.io/en/latest/DOCS.html#xonsh-support">xonsh integration</a>.</p>
<h3 id="plumbum">Plumbum</h3>
<p><a href="https://github.com/tomerfiliba/plumbum">plumbum</a></p>
<p>Plumbum does not attempt to be a shell alternative like xonsh, but instead focus on making the shell scripting experience better, by making external program work as seamless as possible with python code.</p>
<hr />
<p><br/> <br/></p>
<a name="addendum-take-1-anchor"></a>
<h1 style="color:DodgerBlue;">
Take 1
</h1>
<p>I will just shortly address the results of my previous hunt for an alternative to bash scripting. The selections fell on: scsh, luash and mruby</p>
<h2 style="color:MediumVioletRed;">
scsh
</h2>
<p>I pitched this at work, but eventually every one agreed that this was too high a mountain to climb. None of us had scheme experience, and the deadlines was too pressured for us to learn something that was that much different from our C/C++ and bash experience. I mentioned already then, that scsh development seems to have stopped. That appears still to be true</p>
<pre class="sh"><code>❱ git log --date=format:&#39;%Y&#39; --pretty=format:&#39;%ad&#39; | sort | uniq -c | awk &#39;{print $2 &quot;: &quot; $1}&#39;
2009: 123
2010: 44
2011: 28
2012: 25
2013: 20
2014: 47
2015: 3
2016: 1
2022: 2
2024: 2</code></pre>
<a name="luash-2017"></a>
<h2 style="color:MediumVioletRed;">
luash
</h2>
<p>luash appeared abandoned then, so we eventually decided not to proceed. The situation appears to be the same still</p>
<pre class="sh"><code>❱ git log --date=format:&#39;%Y&#39; --pretty=format:&#39;%ad&#39; | sort | uniq -c | awk &#39;{print $2 &quot;: &quot; $1}&#39;
2015: 23
2016: 1
2017: 3</code></pre>
<a name="mruby-2017"></a>
<h2 style="color:MediumVioletRed;">
mruby
</h2>
<p>I brought in mruby for a task that had a size and complexity where I would never use bash, but instead write a C program. The downside of using a compiled program, is that you have to compile, strip and copy to target on each change. This specific task would benefit greatly from being able to make tweaks directly on target, so a scripting language was a perfect fit. I skunk-worked a mruby solution.</p>
<p>What I remember as my takeaways from the experience in 2017 or 2018 of mruby was:</p>
<ol type="1">
<li>Calling external programs was not a first class feature. It was possible, but was not particularly great</li>
<li>The custom plugin system was stellar. It was very easy to write a plugin in C, and the use it from mruby. That was perfect for the task I had to solve</li>
<li>I do remember hitting some bugs. No deal breakers, but yet something that made friction</li>
<li>No one in the team (including me) really fell for the ruby syntax. Shame on us perhaps, but reality is reality</li>
</ol>
<p>I mentioned some bugs and issues… it was a long time ago, and since then the mruby development has only gained more traction. I have nothing but confidence that mruby is working great today. A lot of focus and effort have been channelled into mruby development since then</p>
<pre class="sh"><code>❱ git log --date=format:&#39;%Y&#39; --pretty=format:&#39;%ad&#39; | sort | uniq -c | awk &#39;{print $2 &quot;: &quot; $1}&#39;
2012: 1933
2013: 2279
2014: 2405
2015: 870
2016: 607
2017: 1185
2018: 580
2019: 1411
2020: 1082
2021: 1225
2022: 1116
2023: 851
2024: 399</code></pre>
<p>I haven’t kept up to date with mruby, but at a glance it appears that there have not been innovations in the area of calling external programs. Regular ruby have gotten a library <a href="https://github.com/piotrmurach/tty-command">tty-command</a> that appears to do a good job of simplifying calling external programs. With some limitations it is possible to <a href="https://mruby.org/docs/articles/executing-ruby-code-with-mruby.html">run ruby code from mruby</a>. It might be worth a look…</p>

<p>The post <a href="https://monzool.net/blog/2024/07/14/a-second-search-for-bash-scripting-alternatives/">A Second Search for Bash Scripting Alternatives</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://monzool.net/blog/2024/07/14/a-second-search-for-bash-scripting-alternatives/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>Chose Babashka, DCli or Gleam/Shellout?</title>
		<link>https://monzool.net/blog/2024/01/21/chose-babashka-dcli-or-gleam-shellout/</link>
		
		<dc:creator><![CDATA[monzool]]></dc:creator>
		<pubDate>Sun, 21 Jan 2024 16:59:53 +0000</pubDate>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Shell Scripting]]></category>
		<guid isPermaLink="false">https://monzool.net/blog/?p=827</guid>

					<description><![CDATA[<p>I AM CONTEMPLATING using a scripting language for a small project. In the initial phase, the script will just be a shallow front-end for an existing program which will do all the heavy lifting. With that in mind, a dedicated shell scripting language like bash would seem more fitting. I&#8217;m [&#8230;]</p>
<p>The post <a href="https://monzool.net/blog/2024/01/21/chose-babashka-dcli-or-gleam-shellout/">Chose Babashka, DCli or Gleam/Shellout?</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p><strong>I AM CONTEMPLATING</strong> using a scripting language for a small project. In the initial phase, the script will just be a shallow front-end for an existing program which will do all the heavy lifting. With that in mind, a dedicated shell scripting language like bash would seem more fitting. I&#8217;m expecting to eventually integrate more logic into the script itself over time, which most likely will make bash or similar an unfitting choice in the long run</p>



<p>My immediate contestants are</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e3.png" alt="🟣" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <a href="https://babashka.org">Babashka</a> &#8211;  A clojure variant that emphasize on scripting.</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f535.png" alt="🔵" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <a href="https://dcli.onepub.dev">DCli</a> &#8211; A dart library for making cli&#8217;s.</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e2.png" alt="🟢" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <a href="https://github.com/tynanbe/shellout">Shellout</a> &#8211; A library adding shell operations to Gleam.</p>



<p>I have next to nothing experience in these languages, but I guess this would be a fine opportunity to learn one of them. Actually I had a fourth contestant, but I dont know OCaml and its documentation was just to sparse for me to evaluate if this was any good or not</p>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f534.png" alt="🔴" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <a href="https://github.com/janestreet/shexp">Shexp</a></p>



<p>I&#8217;m in doubt which language I should pick up. If I had time for it, I would try them all and compare, but unfortunately I do not <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f937-200d-2640-fe0f.png" alt="🤷‍♀️" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Any suggestions on which would be the better choice?</p>
<p>The post <a href="https://monzool.net/blog/2024/01/21/chose-babashka-dcli-or-gleam-shellout/">Chose Babashka, DCli or Gleam/Shellout?</a> appeared first on <a href="https://monzool.net/blog">Monzool&#039;s Personal Publishing</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
