<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="/css/rss-browser.css" ?>
<rss version="2.0">
	<channel>
		<title>Perturb.org - Scott's Geek Stuff</title>
		<link>http://www.perturb.org/</link>
		<description>Just Geek Stuff</description>

		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1464/</guid>
			<title>Linux: List running services</title>
			<link>http://www.perturb.org/display/entry/1464/</link>
			<description>&lt;p&gt;Just came across this quick command to show running services.&lt;/p&gt;
		&lt;pre&gt;&lt;code&gt;systemctl list-units --type=service --state=running&lt;/code&gt;&lt;/pre&gt;</description>
			<pubDate>Sat, 19 Sep 2026 08:57:22 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1463/</guid>
			<title>Perl: Split a string on a delimiter, but respect quotes</title>
			<link>http://www.perturb.org/display/entry/1463/</link>
			<description>&lt;p&gt;It&#039;s a common practice to split a string on a delimiter like a comma or semi-colon. If your input data contains the delimiter in quotes you need to respect that and &lt;strong&gt;not&lt;/strong&gt; split there. I wrote a simple function to handle 99% of the use cases for splitting in this manner.&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;quote_split(&#039;foo, bar, baz&#039;);   # (&#039;foo&#039;, &#039;bar&#039;, &#039;baz&#039;)
		quote_split(&quot;foo, &#039;bar, baz&#039;&quot;); # (&#039;foo&#039;, &#039;bar, baz&#039;)&lt;/code&gt;&lt;/pre&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;# Split a string on commas, but respect single and double quotes with commas
		sub quote_split {
		    my ($str, $separator) = @_;
		    $separator //= &quot;,&quot;;
		    if (!length($separator)) {
		        die(&quot;quote_split separator cannot be empty\n&quot;);
		    }
		
		    my $separator_re = quotemeta($separator);
		    my @items;
		
		    while ($str =~ /\G\s*(?:
		        &quot;((?:\\.|[^&quot;\\])*)&quot;   # Double-quoted
		        | &#039;((?:\\.|[^&#039;\\])*)&#039; # Single-quoted
		        | ((?:(?!$separator_re)[\s\S])+) # Unquoted
		    )\s*(?:$separator_re|\z)/gcx) {
		
		        my $item = $1 // $2 // $3;
		
		        push(@items, $item);
		    }
		
		    return @items;
		}&lt;/code&gt;&lt;/pre&gt;</description>
			<pubDate>Fri, 04 Sep 2026 16:24:55 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1462/</guid>
			<title>Perl: Basic YAML parsing in a copy/pasteable function</title>
			<link>http://www.perturb.org/display/entry/1462/</link>
			<description>&lt;p&gt;I need very basic YAML parsing in Perl, mostly for reading configuration files. There aren&#039;t any great options in Perl core, and I only need a subset of YAML (basic scalars, arrays, and hashes), so I worked up a simple copy/pasteable function. &lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;my $x = yaml_parse($yaml_str);&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;This implementation supports: nesting, scalars, arrays, and hashes, but is missing &lt;code&gt;null&lt;/code&gt; / &lt;code&gt;~&lt;/code&gt;, booleans, and complex quotes.&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;sub yaml_parse {
		    return {} unless defined $_[0] &amp;amp;&amp;amp; length $_[0];
		    my %data; my $root = \%data; my @st = ([-1, \$root]);
		    for my $line (split /\n/, $_[0]) {
		        $line =~ s/\r$//;
		        next if $line =~ /^\s*(?:$|---\s*$|#)/;
		        if ($line =~ /^(\s*)-\s*(.*)$/) {
		            my ($n, $v) = (length($1), $2);
		            pop @st while $n &amp;lt; $st[-1][0]; my $cur = ${$st[-1][1]};
		            die &quot;yaml_parse: array without parent: $line&quot; if @st == 1;
		            $cur = ${$st[-1][1]} = [] if ref $cur eq &#039;HASH&#039; &amp;amp;&amp;amp; !%$cur;
		            die &quot;yaml_parse: mixed array/hash&quot; if ref $cur ne &#039;ARRAY&#039;;
		            $v =~ s/^\s+|\s+$//g; $v =~ s/^([&#039;&quot;])(.*)\1$/$2/s;
		            $v += 0 if $v =~ /^-?\d+(?:\.\d+)?$/; if ($v =~ /^([\w\-\.\/]+):\s*$/) {
		            push(@$cur, my $el = {$1 =&amp;gt; {}}); push(@st, [$n + 2, \$el-&amp;gt;{$1}]); next; }
		            push(@$cur, $v);
		        } elsif ($line =~ /^(\s*)([\w\-\.\/]+)\s*:\s*(.*)$/) {
		            my ($n, $key, $v) = (length($1), $2, $3);
		            pop @st while $n &amp;lt;= $st[-1][0]; my $cur = ${$st[-1][1]};
		            die &quot;yaml_parse: &#039;$key&#039; under array&quot; if ref $cur eq &#039;ARRAY&#039;;
		            $v =~ s/\s+$//; $v =~ s/^\s+//; $v =~ s/^([&#039;&quot;])(.*)\1$/$2/s;
		            if ($v =~ /^\[(.*)\]$/) {
		                my @a = grep { length } map { s/^\s+|\s+$//gr =~ s/^([&#039;&quot;])(.*)\1$/$2/sr } split /,/, $1;
		                for (@a) { $_ += 0 if /^-?\d+(?:\.\d+)?$/ } $v = \@a;
		            } else { $v += 0 if $v =~ /^-?\d+(?:\.\d+)?$/; }
		            if (ref $v or length $v) { $cur-&amp;gt;{$key} = $v }
		            else { $cur-&amp;gt;{$key} = {}; push(@st, [$n, \$cur-&amp;gt;{$key}]) }
		        } else { warn &quot;yaml_parse: ignoring: $line\n&quot; }
		    } return \%data;
		}&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;&lt;code&gt;YAML::XS&lt;/code&gt; is definitely the best Perl YAML parser, but it can be overkill if all you need is simple parsing.&lt;/p&gt;
		&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I wrote some &lt;a href=&quot;https://www.perturb.org/code/yaml_parse.pm&quot;&gt;unit tests&lt;/a&gt; to go along with this.&lt;/p&gt;</description>
			<pubDate>Wed, 26 Aug 2026 13:51:35 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1461/</guid>
			<title>Javascript: Sluz sandbox</title>
			<link>http://www.perturb.org/display/entry/1461/</link>
			<description>&lt;p&gt;I ported my &lt;a href=&quot;https://github.com/scottchiefbaker/sluz&quot;&gt;Sluz&lt;/a&gt; templating engine from PHP to &lt;a href=&quot;https://github.com/scottchiefbaker/js-Template-Sluz&quot;&gt;JavaScript&lt;/a&gt;. It&#039;s now possible to run a full Sluz installation entirely in-browser. As a proof-of-concept I ported the &lt;a href=&quot;https://www.perturb.org/code/sluz-sandbox-js/&quot;&gt;Sluz Sandbox&lt;/a&gt; to use the JS version of library. Now you can test and validate Sluz code 100% in-browser.&lt;/p&gt;</description>
			<pubDate>Wed, 19 Aug 2026 18:50:14 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1460/</guid>
			<title>Hardware required to run on frontier level AI model</title>
			<link>http://www.perturb.org/display/entry/1460/</link>
			<description>&lt;p&gt;AI models like Claude Opus and ChatGPT 5.6 are called &quot;frontier&quot; (i.e. top of the line). There are several fully open source frontier level AI models that are available to download for free. I ran the specs on what it would take to run a frontier level model.&lt;/p&gt;
		&lt;blockquote&gt;
		&lt;p&gt;For inference of a frontier model (400B-1T+ params):&lt;/p&gt;
		&lt;ul&gt;
		&lt;li&gt;Minimum (4-bit quantized): ~200-500GB VRAM -&amp;gt; 4-8x H100 80GB GPUs&lt;/li&gt;
		&lt;li&gt;FP16/half precision: ~800GB-2TB VRAM -&amp;gt; 10-32x H100 80GB GPUs&lt;/li&gt;
		&lt;li&gt;System RAM: 512GB+&lt;/li&gt;
		&lt;li&gt;Interconnect: NVLink or InfiniBand between GPUs&lt;/li&gt;
		&lt;li&gt;Storage: Several hundred GB for model weights
		For training: 10,000-100,000+ GPU-hours on H100-class hardware, multi-million dollar cluster, weeks of continuous run time.
		No single consumer GPU can run a frontier model - even an RTX 4090 (24GB) is about 10-30x short of VRAM needed for even a heavily quantized frontier model.&lt;/li&gt;
		&lt;/ul&gt;
		&lt;/blockquote&gt;
		&lt;p&gt;If you just want to host a &quot;good&quot; AI model it requires 8x GPUs with 80GB of VRAM each.&lt;/p&gt;
		&lt;p&gt;It&#039;s about $300k to get in the door for a SINGLE server to run AI you can ask questions&lt;/p&gt;</description>
			<pubDate>Thu, 13 Aug 2026 13:50:43 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1459/</guid>
			<title>Perl: romuduojr PRNG</title>
			<link>http://www.perturb.org/display/entry/1459/</link>
			<description>&lt;p&gt;Another day, another PRNG ported to Perl. Today is &lt;a href=&quot;https://www.perturb.org/code/romujr.pl&quot;&gt;romuduojr&lt;/a&gt; from &lt;a href=&quot;https://www.romu-random.org/&quot;&gt;romu-random.org&lt;/a&gt;. Pretty simple 64bit PRNG.&lt;/p&gt;</description>
			<pubDate>Thu, 13 Aug 2026 09:37:13 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1458/</guid>
			<title>Perl: A simple module that doubles as a script</title>
			<link>http://www.perturb.org/display/entry/1458/</link>
			<description>&lt;p&gt;I have a small Perl script that defines a handful of functions. Perl allows you to make a library that doubles as a script when called directly. Using &lt;code&gt;caller()&lt;/code&gt; you can determine if your script was loaded via a &lt;code&gt;require()&lt;/code&gt; call, or called directly. This allows you to export functions if called as a library, but run code if called directly via: &lt;code&gt;perl my_lib.pl&lt;/code&gt;.&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;# my_lib.pl
		if (!caller()) {
		    say greet(&quot;Scott&quot;);
		}
		
		sub greet {
		    my $name = shift();
		
		    return &quot;Hello $name&quot;;
		}
		
		1; # Required if you load as a library&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;Use a &lt;code&gt;require()&lt;/code&gt; call to load the module and get access to the &lt;code&gt;greet()&lt;/code&gt; function.&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;# main.pl
		require(&quot;/path/my_lib.pl&quot;);
		
		say greet(&quot;Foo&quot;);&lt;/code&gt;&lt;/pre&gt;</description>
			<pubDate>Sat, 01 Aug 2026 12:02:05 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1457/</guid>
			<title>Summary of each book in the Odyssey</title>
			<link>http://www.perturb.org/display/entry/1457/</link>
			<description>&lt;h3&gt;The Odyssey by Homer&lt;/h3&gt;
		&lt;ol&gt;
		&lt;li&gt;Odysseus&#039;s house is full of suitors eating all his food and drinking his wine waiting for Athena to choose a new husband&lt;/li&gt;
		&lt;li&gt;Telemachus calls a council of suitors to try and get rid of them, but the mock him and decide to stay&lt;/li&gt;
		&lt;li&gt;Telemachus sails to Pylos to meet King Nestor and ask about his father&#039;s Whereabouts&lt;/li&gt;
		&lt;li&gt;Telemachus reaches Sparta and learns his father is trapped on Calypso&#039;s island. The suitors plan ambush and kill Telemachus when he returns&lt;/li&gt;
		&lt;li&gt;Odysseus builds a raft and sails for 17 days besieged by Poseidon&#039;s wrath. The raft is destroyed and he swims to shore.&lt;/li&gt;
		&lt;li&gt;Nausicaa finds Odysseus and takes him to her father&#039;s house for a weird washing party. She directs him to her royal parents.&lt;/li&gt;
		&lt;li&gt;The king senses Odysseus greatness and offers his daughter in marriage but Odysseus declines and only wants to return home.&lt;/li&gt;
		&lt;li&gt;The Phaeacian compete in sports style games and recruit Odysseus. He says he is too old and worn out from the war.&lt;/li&gt;
		&lt;li&gt;Odysseus gets captured by a huge cyclops and kept in a cave. Odysseus blinds the Cyclops with a flame stick and escapes to his boat taunting the cyclops as he leaves.&lt;/li&gt;
		&lt;li&gt;Circe turns Odysseus&#039;s men into pigs and tempts Odysseus into her bed. He makes her swear an oath she won&#039;t harm him on his quest. She tells them they must go to the underworld next.&lt;/li&gt;
		&lt;li&gt;Odysseus travels to the underworld and meets on his dead crewmen who fell off Circe&#039;s roof and broke his neck. He also meets a ghost who looks like his mother and it causes him grief.&lt;/li&gt;
		&lt;li&gt;Odysseus is warned about the Siren&#039;s call. He tells his men to bind him to the mast and ignore his pleas for freedom. The men put wax in their ears and row past the Sirens. His men, starving, slaughter a sacred cow so Zeus destroys their ship and drowns everyone except Odysseus.&lt;/li&gt;
		&lt;li&gt;Odysseus makes it back to Ithaca, but he doesn&#039;t recognize the location. Athena transforms him into a shepherd and tells him to go see the man in charge of his pigs.&lt;/li&gt;
		&lt;li&gt;Odysseus meets the swineherd who welcomes him warmly without recognizing him. They share a meal and stories of his travels.&lt;/li&gt;
		&lt;li&gt;Telemachus heads back to Ithaca after Athena warns him of the suitors ambush. Odysseus reveals his identity to the swineherd and they head to Oddysseus&#039;s home.&lt;/li&gt;
		&lt;li&gt;Telemachus arrives at the swineherds home and meets his father who is still disguised. The reunite and plot to murder the suitors.&lt;/li&gt;
		&lt;li&gt;The three travel to Odysseus&#039;s home and the goatherd mocks and kicks him. Odysseus ignores him and meets his dog who recognizes him even in disguise. The suitors assault and insult the disguised Odysseus.&lt;/li&gt;
		&lt;li&gt;A beggar arrives at the house and challenges Odysseus, but Odysseus knocks his down pretty easily. Eurymachus throws a stool at Odysseus, while he observes the suitors behavior.&lt;/li&gt;
		&lt;li&gt;Odysseus and Telemachus remove the weapons from the hall. Nurse Eurycleia recognizes the disguised Odysseus by a scar on his foot, but he swears her to secrecy.&lt;/li&gt;
		&lt;li&gt;Theoclymenus foresees doom for the suitors, prophesying their impending death, but they mock him and dismiss his warning.&lt;/li&gt;
		&lt;li&gt;Penelope brings out Odysseus&#039;s great bow and announces a contest to win her hand. The suitors all fail to string the bow, Odysseus steps up, strings the bow and shoots the arrow through twelve axes heads and wins the contest.&lt;/li&gt;
		&lt;/ol&gt;</description>
			<pubDate>Wed, 15 Jul 2026 07:57:37 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1456/</guid>
			<title>Git: Keep two branches in sync</title>
			<link>http://www.perturb.org/display/entry/1456/</link>
			<description>&lt;p&gt;I have a Git repository with a primary branch that I maintain actively. I also have a separate feature branch that I&#039;d like to keep in sync with the main branch, and alert if there are any conflicts &lt;em&gt;immediately&lt;/em&gt;. Using Git hooks you can script automatically pulling each new commit from the primary branch to the feature branch.&lt;/p&gt;
		&lt;p&gt;Create a &lt;code&gt;.git/hooks/post-commit&lt;/code&gt; file and put these contents in it:&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/sh
		
		SRC=&quot;main&quot;
		DST=&quot;feature&quot;
		
		branch=$(git branch --show-current)
		
		if [ &quot;$branch&quot; = &quot;$SRC&quot; ]; then
		    commit=$(git rev-parse HEAD)
		
		    git checkout $DST &amp;amp;&amp;amp;
		    git cherry-pick &quot;$commit&quot; &amp;amp;&amp;amp;
		    git checkout main
		fi&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;If there are conflicts, Git errors out immediately and leaves you on the feature branch to manually resolve the conflict.&lt;/p&gt;</description>
			<pubDate>Mon, 15 Jun 2026 09:24:42 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1455/</guid>
			<title>Using rsync to keep two directories in sync</title>
			<link>http://www.perturb.org/display/entry/1455/</link>
			<description>&lt;p&gt;I have two directories with (mostly) the same content that I want to keep in sync. Specifically I want to make sure that the newest version of each file is synced to the other directory. This allows me to update a file on either side, and that version will propagate to the other. You can do this with a bi-directional &lt;code&gt;rsync&lt;/code&gt; command:&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rsync --update -av /dir/a/ /dir/b/
		rsync --update -av /dir/b/ /dir/a/&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;Using &lt;code&gt;--update&lt;/code&gt; tells &lt;code&gt;rsync&lt;/code&gt; to skip files on the receiving side that are newer. If you sync &lt;code&gt;a&lt;/code&gt; -&amp;gt; &lt;code&gt;b&lt;/code&gt; and then &lt;code&gt;b&lt;/code&gt; -&amp;gt; &lt;code&gt;a&lt;/code&gt; you end up with both locations having the newest copy of each file.&lt;/p&gt;</description>
			<pubDate>Wed, 20 May 2026 11:40:26 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1454/</guid>
			<title>Perl: ULID generation</title>
			<link>http://www.perturb.org/display/entry/1454/</link>
			<description>&lt;p&gt;&lt;a href=&quot;https://github.com/ulid/spec&quot;&gt;ULID&#039;s&lt;/a&gt; are an interesting way to generate globally unique identifiers. Here is a quickie Perl implementation to generate ULIDs. This implementation does &lt;strong&gt;not&lt;/strong&gt; include the intra-millisecond monotonic increment however. If that feature is important to you consider checking out a more full-featured implementation like &lt;a href=&quot;https://github.com/scottchiefbaker/perl-ULID-Tiny&quot;&gt;ULID::Tiny&lt;/a&gt;.&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;for (1 .. 5) {
		    say(ulid());
		}&lt;/code&gt;&lt;/pre&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;sub ulid {
		    my $ts    = $_[0] || time() * 1000;
		    my $bytes = substr(pack(&quot;Q&amp;gt;&quot;, $ts), 2, 6);
		
		    # Append 10 random bytes
		    $bytes .= pack &#039;C*&#039;, map { int(rand(256)) } 1 .. 10;
		
		    # base32 encoding
		    my $bits  = &#039;00&#039; . unpack(&#039;B*&#039;, $bytes);
		
		    # Chars to use for base32
		    my @CROCKFORD_CHARS = split(//, &#039;0123456789ABCDEFGHJKMNPQRSTVWXYZ&#039;);
		
		    my $result  = &#039;&#039;;
		    for (my $i = 0; $i &amp;lt; 130; $i += 5) {
		        my $index = oct(&#039;0b&#039; . substr($bits, $i, 5));
		        $result .= $chars[$index];
		    }
		
		    return $result;
		}&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;&lt;strong&gt;See also:&lt;/strong&gt; &lt;a href=&quot;https://www.perturb.org/display/1398_Perl_UUIDv7.html&quot;&gt;UUIDv7&lt;/a&gt;&lt;/p&gt;</description>
			<pubDate>Sun, 26 Apr 2026 18:58:48 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1453/</guid>
			<title>Perl: Using Inline::C to embed C functions in your Perl scripts</title>
			<link>http://www.perturb.org/display/entry/1453/</link>
			<description>&lt;p&gt;Perl allows &quot;inline&quot; code written in other languages. This is useful because you can have native C functions interact with your Perl code. By placing your C code in the &lt;code&gt;__DATA__&lt;/code&gt; section of your Perl script you get clean separation between the two languages.&lt;/p&gt;
		&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;Inline::C&lt;/code&gt; does not understand &lt;code&gt;uint64_t&lt;/code&gt; in function definitions, so anything that interacts with Perl needs to use &lt;code&gt;UV&lt;/code&gt; instead. Internally C functions can use and interact with &lt;code&gt;uint64_t&lt;/code&gt; variables just fine.&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-perl&quot;&gt;use strict;
		use warnings;
		use v5.16;
		use Inline &#039;C&#039;;
		
		##############################################
		
		seed_splitmix64(time());
		
		for (1 .. 5) {
		    say splitmix64();
		}
		
		##############################################
		
		__DATA__
		__C__
		
		uint64_t x = 123456789;
		
		void seed_splitmix64(UV seed) {
		    x = seed;
		}
		
		UV splitmix64() {
		    uint64_t z = (x += 0x9e3779b97f4a7c15);
		    z = (z ^ (z &amp;gt;&amp;gt; 30)) * 0xbf58476d1ce4e5b9;
		    z = (z ^ (z &amp;gt;&amp;gt; 27)) * 0x94d049bb133111eb;
		
		    return z ^ (z &amp;gt;&amp;gt; 31);
		}&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;This will compile the C code into a shared object in the &lt;code&gt;_Inline&lt;/code&gt; directory in whichever directory you instantiated your Perl script. Code is only compiled once (and where there are changes), so your script performance will be very high.&lt;/p&gt;</description>
			<pubDate>Thu, 23 Apr 2026 14:34:43 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1452/</guid>
			<title>Comparison of markup languages</title>
			<link>http://www.perturb.org/display/entry/1452/</link>
			<description>&lt;p&gt;I&#039;ve been investigating &lt;a href=&quot;https://www.json.org/&quot;&gt;JSON&lt;/a&gt; vs &lt;a href=&quot;https://yaml.org/&quot;&gt;YAML&lt;/a&gt; vs &lt;a href=&quot;https://toml.io/&quot;&gt;TOML&lt;/a&gt; for various applications. After testing many variations I ended up writing a &lt;a href=&quot;https://www.perturb.org/code/js-serializer/&quot;&gt;simple tool&lt;/a&gt; to compare all three live.&lt;/p&gt;</description>
			<pubDate>Thu, 09 Apr 2026 10:58:41 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1451/</guid>
			<title>Linux: SFTP and SCP friendly login banners</title>
			<link>http://www.perturb.org/display/entry/1451/</link>
			<description>&lt;p&gt;To display a message when a user logs into your server, add the following to &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;/p&gt;
		&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# If it&#039;s an interactive terminal show the banner
		if [[ $- == *i* ]]; then
		    echo &quot;Welcome to the monitoring server&quot;
		    echo &quot;Configuration is stored in /etc/myapp&quot;
		fi&lt;/code&gt;&lt;/pre&gt;
		&lt;p&gt;This runs &lt;em&gt;only&lt;/em&gt; for interactive sessions. Non-interactive connections such as SFTP and SCP remain unaffected, which prevents automated processes from breaking.&lt;/p&gt;
		&lt;p&gt;&lt;strong&gt;See also:&lt;/strong&gt; The &lt;a href=&quot;https://github.com/scottchiefbaker/text_color&quot;&gt;text_color&lt;/a&gt; project.&lt;/p&gt;</description>
			<pubDate>Thu, 26 Mar 2026 14:07:00 -0700</pubDate>
		</item>
		
		<item>
			<guid isPermaLink="true">http://www.perturb.org/display/entry/1450/</guid>
			<title>YAML is growing on me</title>
			<link>http://www.perturb.org/display/entry/1450/</link>
			<description>&lt;p&gt;The more I learn about YAML, the more I like it. JSON is great as a machine readable format, but it sucks at being human readable. Want to add an element, better make sure you have &lt;strong&gt;all&lt;/strong&gt; the commas and curly braces in the &lt;strong&gt;exact&lt;/strong&gt; right place or the whole thing will be unusable. YAML on the other hand is designed to be human readable and modifiable. It&#039;s a very simple key/value system using indentation to represent layers. &lt;/p&gt;
		&lt;p&gt;PHP has a &lt;a href=&quot;https://bd808.com/pecl-file_formats-yaml/&quot;&gt;PECL module&lt;/a&gt; with good YAML support. There are also pure PHP versions if you&#039;re unable to install PECL modules. &lt;a href=&quot;https://github.com/symfony/yaml&quot;&gt;Symfony&lt;/a&gt; provides one, and so does &lt;a href=&quot;https://github.com/mustangostang/spyc&quot;&gt;Spyc&lt;/a&gt;. I prefer the latter because it&#039;s a single file and very easy to install.&lt;/p&gt;
		&lt;p&gt;On the Perl side there is &lt;a href=&quot;https://metacpan.org/pod/YAML::XS&quot;&gt;YAML::XS&lt;/a&gt;, &lt;a href=&quot;https://metacpan.org/pod/YAML::PP&quot;&gt;YAML::PP&lt;/a&gt;, and &lt;a href=&quot;https://metacpan.org/search?size=20&amp;amp;q=yaml&quot;&gt;many&lt;/a&gt; others. &lt;/p&gt;
		&lt;p&gt;Parsing YAML is very easy in just about every language I can find. If you have a complex data structure that you need humans to interact with use YAML please.&lt;/p&gt;
		&lt;p&gt;Here is a &lt;a href=&quot;https://jsonlint.com/json-vs-yaml&quot;&gt;great breakdown&lt;/a&gt; of when to use YAML vs JSON:&lt;/p&gt;
		&lt;table&gt;
		&lt;thead&gt;
		&lt;tr&gt;
		&lt;th&gt;Use Case&lt;/th&gt;
		&lt;th&gt;Recommended&lt;/th&gt;
		&lt;th&gt;Why&lt;/th&gt;
		&lt;/tr&gt;
		&lt;/thead&gt;
		&lt;tbody&gt;
		&lt;tr&gt;
		&lt;td&gt;API request/response&lt;/td&gt;
		&lt;td&gt;JSON&lt;/td&gt;
		&lt;td&gt;Universal support, strict parsing&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
		&lt;td&gt;Configuration files&lt;/td&gt;
		&lt;td&gt;YAML&lt;/td&gt;
		&lt;td&gt;Comments, readability&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
		&lt;td&gt;Browser/JavaScript&lt;/td&gt;
		&lt;td&gt;JSON&lt;/td&gt;
		&lt;td&gt;Native parsing&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
		&lt;td&gt;Kubernetes/Docker&lt;/td&gt;
		&lt;td&gt;YAML&lt;/td&gt;
		&lt;td&gt;Industry standard&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
		&lt;td&gt;Data interchange&lt;/td&gt;
		&lt;td&gt;JSON&lt;/td&gt;
		&lt;td&gt;Unambiguous, fast&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
		&lt;td&gt;Human-edited files&lt;/td&gt;
		&lt;td&gt;YAML&lt;/td&gt;
		&lt;td&gt;Less punctuation&lt;/td&gt;
		&lt;/tr&gt;
		&lt;/tbody&gt;
		&lt;/table&gt;
		&lt;p&gt;&lt;a href=&quot;https://perlpunk.github.io/yaml-test-schema/schemas.html&quot;&gt;YAML spec&lt;/a&gt; differences.&lt;/p&gt;</description>
			<pubDate>Wed, 25 Mar 2026 10:04:29 -0700</pubDate>
		</item>
		
	</channel>
</rss>
