Sed challenge: join cal -y months into a single column
jol.dev
jol.dev
cal ← ⎕SH 'cal -y' ⍝ get output of cal command
bymonth ← {⍵⊂⍨0=23|⍳≢⍵}⍤1↑2↓cal ⍝ partition into month groups
lines ← {⍵⊂⍨0=8|⍳≢⍵}⍤1⍉bymonth ⍝ partition into chunks in each line
months ← ~∘' '¨,⍉⊃¨lines ⍝ extract month names and remove spaces
days ← (23⍴1 0 0)⊂⍤1↑⊃,/,⌿2↓¨lines ⍝ partition into days
trimmed ← ∊⍤1⍉{⍵/⍨(' '∨.≠¨⍵)∨∧\' '∧.=¨⍵}⍤1⍉¯1↓days ⍝ remove excess spaces
⎕←53↑(¯2↓⍤1⊢trimmed) , ↑months\⍨∨/' 1 '⍷trimmed ⍝ append months to calendar where the 1s are, and print
Surprisingly tricky (but still easier than sed IMO :P)I'm decent with sed. I wouldn't call myself an expert, but know how to employ addressing and hold spaces, which are sadly aracana to most people who employ sed. I ended up learning sed because it was there, proved useful, and it fit into the command-line toolkit really well. (In order of importance.) APL would never slot into that toolkit as well as other tools, but it's value-add would more than compensate. Unfortunately, the value-add isn't enough to compensate for the fact that it's not there and in your face every time you're exploring a problem and its solution space.
I think the major issue is that APL seems to require more than ASCII to work it.
https://en.wikipedia.org/wiki/APL_(programming_language)#GNU...
> (...) Richard Stallman, founder of the GNU Project, was an early adopter of APL, using it to write a text editor as a high school student in the summer of 1969.
And:
Rho, rho, rho of X
Always equals 1
Rho is dimension, rho rho rank.
APL is fun!
Richard M. Stallman, 1969
https://www.gnu.org/software/apl/But:
> GNU APL was written and is being maintained by Jürgen Sauermann.
and I believe it's fairly young when compared to emacs/emacs lisp.
Yet, here we are. I'm still amazed to see people using it, and even more amazed to see someone break it out for some toy play.
Still, not gonna learn it. I like my hair.
Similarly, the lines assignment needed to be {⍵⊂⍨0=9|⍳≢⍵}⍤1⍉bymonth.
After those changes it worked OMM, but the month names were appended directly to the end of the first Saturday's date with no space (e.g. "1 2January"). So I changed ↑months to ↑(' ',¨months) in the last line to insert a space there (there's likely a better way to do that; I'm still pretty new at APL).
I added `-h` to make sure `cal` didn't highlight today's date, which can throw things off, and eliminated intermediate vars that were only used once; this was what I wound up with (works from a clean ws in Dyalog OMM):
⎕io ← 0
lines ← {⍵⊂⍨0=9|⍳≢⍵}⍤1⍉{⍵⊂⍨0=22|⍳≢⍵}⍤1↑1↓⎕sh 'cal -hy'
months ← ~∘' '¨,⍉⊃¨lines
days ← ∊⍤1⍉{⍵/⍨(' '∨.≠¨⍵)∨∧\' '∧.=¨⍵}⍤1⍉¯1↓(23⍴1 0 0)⊂⍤1↑⊃,/,⌿2↓¨lines
⎕←53↑(¯2↓⍤1⊢days) , ↑(' ',¨months)\⍨∨/' 1 '⍷daysPotentially cal -y on macOS produces a different shape? What's ⍴↑cal?
Try doing cal←⎕SE.UCMD'Calendar 2021' and replace ↑2↓cal with 1↓cal - potentially that'll work?
What system are you on? On Ubuntu, cal -y | wc -l also gives me 36.
#!/bin/sh
g() {
read m; read n
[ $1 -eq 1 ] \
&& printf '%20.20s %s\n' "$n" "$m" \
|| printf ' %s %s\n' "$n" "$m"
sed -e 's/ *$/ /'
}
f() {
cal -1 $@ | grep '[0-9]' | g $@ | head -c -1
}
y=${1:-`date +%Y`}
for m in {1..12}; do f $m $y; done
printf "\n"I can also empathize with the desire for a single-column calendar output, but my first instinct wasn't to implement it with sed:
Substantially more lines than the sed implementation, but also more feature rich, because I can do things like
calz from last month to next month
calz from now to feb
etc. Also: pretty colors.Thanks! You can access the org file by changing the URL extension from .html to .org. Maybe I should make that a link.
... I will force myself not to make use of it because you're a bad influence and I already have to stop myself from using too much grep/sed/awk magic, but this is seriously impressive, and an entertaining read. Well done!
#!/bin/sh -eu
set -o pipefail
sed -E \
-e 's/([+*])\s*([0-9]+)/ \2 \1 /g' \
-e ':parens' \
-e 's/([+*])\s*(\([^()]+\))/ \2 \1 /g' \
-e 's/\(([^()]+)\)/ \1 /g' \
-e '/\(/bparens' \
-e 's/\s+/ /g' -e 's/^ //' -e 's/ $//' \
-e '$a\' -e '[+ z1<r]sr z1<r p' | \
dc
(I’m very, very sorry for fuelling your addiction.)UPD: The original version claimed this was a precedence parser. It isn’t, it just does parentheses :(
And accompanying blog post in Russian https://habr.com/ru/post/191006/.
say qx< cal -hy > # Command output as string
.lines.skip.join("\n").split(/\n\n+/) # Skip year & split groups
.map({ |([Z] .lines.join.comb(22).batch(3)) }) # Single column structure
.map({ |(.[2] ~ .[0].trim, |.[3 .. *]) }) # Month name on first row
.grep(*.trim.chars).join("\n") # Join non-blank lines
.subst(/\s+ ^^ \s\s+ 1»/, ' 1', :g) # Collapse weeks together
I was never going to beat APL... Though I imagine some clever Rakoons could probably find a few places where this can be golfed down. say qx< cal -hy >.lines.skip.batch(9)
.map({ |([Z] .join.comb(22).batch(3)) })
.map({ |(.[2] ~ .[0].trim, |.[3 .. *]) })
.grep(*.trim.chars).join("\n")
.subst(/\s+ ^^ \s\s+ 1»/, ' 1', :g) $ echo $(((($(date +%s -d 'Aug 31') - $(date +%s -d 'Jul 1'))/(60*60*24*7))))
8 # 8.7 weeks truncated
$ echo $(((($(date +%W -d 'Aug 31') - $(date +%W -d 'Jul 1')))))
9
It's useful to get the number of weeks from git logs for release notes etc., which can be done like: oldvs='v1.0'
oldts=$(git log -n1 --format="%at" "$oldvs")
weeks=$((($(date +%s) - $oldts)/(60*60*24*7)))Now, given that this confounded problem has taken too much of my time @^_^@ I'll just say that the simplest option seems to be
ncal -w -3 -y -b -h
because it already computes the week numbers. Fiddle away with sed afterward.
By the way, the "fold" utility [1] can help here too:
ncal -w -3 -y -b -h | sed 's/ /*/g' | fold -w 27
Cheers!
I really enjoy these kinds of issues and see some wizards (like this post!) be able to do some really cool stuff, and I’d like to have it in my tool belt, but I’m not sure where I can get practice problems.
Thanks!
This will not work with BSD or Plan9 versions of sed
They do not support using newline characters in a substitution command.
Also, the other seds do not have the GNU "steps" extension, e.g., 2~3. The blog author's solution strategy is 100% reliant on this "steps" feature.
I was even hoping there'd be comments that show a better solution to learn from them, GNU or not. I got an APL one at least, so that was cool.
This should work with GNU sed, BSD sed and Plan9 sed.
#!/bin/sh
col1(){ cal -y|sed -n "$1{s/\(.\{23\}\)\(.*\)/\1/p;}";}
col2(){ cal -y|sed -n "$1{ s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/p;}";}
col3(){ cal -y|sed -n "$1{ s/.\{46\}//p;}";}
cal_y(){ for x in 3,10 11,18 19,26 27,34;do col1 "$x";col2 "$x";col3 "$x";done;}
test $# -eq 4||exec echo usage: $0 7 jul 20 aug;
x=$(echo $2 $4|sed -n '/[A-Z]/p');
if test "$x";then echo use lowercase;else
cal_y|sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/
/Jan/,/Feb/s/$/ jan /;
/Feb/,/Mar/s/$/ feb /;
/Mar/,/Apr/s/$/ mar /;
/Apr/,/May/s/$/ apr /;
/May/,/Jun/s/$/ may /;
/Jun/,/Jul/s/$/ jun /;
/Jul/,/Aug/s/$/ jul /;
/Aug/,/Sep/s/$/ aug /;
/Sep/,/Oct/s/$/ sep /;
/Oct/,/Nov/s/$/ oct /;
/Nov/,/Dec/s/$/ nov /;
/Dec/,$s/$/ dec /;/@/d' \
|sed -n "/ $1 .*$2/,/ $3 .*$4/{
/ 1 /d;p;}"|sed -n '$=';fiNo, it isn't! :)
The challenge was specifically about joining the months of `cal -y` into a single column with sed. It's about the challenge of working with a grid of grids, with the restrictive nature of sed, having only one hold-space to hold 2 months separately. You totally missed doing that with your solution. Of course, it's loads easier to just skip the challenge altogether!
Maybe the point of the sed challenge wasn't clear. It's about stretching the abilities of sed to its limits. Of being able to use all its features to its fullest. I think this is the first time that I had a real use for m~n, and I still haven't found a good use for `D` or `P`.
EDIT: You're using `cal -y` now, but you've still gone outside the challenge space:
> what kind of sed script I’d need in the middle of a `cal -y | ... | wc -l` pipeline to get the same answer
Meaning the working space is just substituting the ellipses for `sed` commands. It's a single `cal -y` call, a single `wc -l` call, and no other shell code except pipes.
I did an awk command in the last bit, but here is the sed equivalent:
sed -E '
/[a-z]$/{h;s/.* //;x}
/[a-z]$/!G
/\b7\b.*July/,/20.*August/!d
s/\n.*//
'
Ideally, it was supposed to be just a single sed command, but I wanted to try out the org-mode blocks for showing the process in multiple stages and also use the m~n addresses, which it seemed like I wouldn't be able to if I did the single call.111 is the full calendar, the others are individual months.
No dependence on any GNU sed features. Will work with GNU sed, BSD sed and Plan9 sed.
Uses only sed commands and pipes, single cal -y, sed instead of wc.
test $# -eq 4||exec echo usage: $0 7 jul 20 aug;
x=$(echo $2 $4|sed -n '/[A-Z]/p');
if test "$x";then echo use lowercase;exit;fi
cal -y \
|sed -n -e "w 111" \
-e "3,10{s/\(.\{23\}\)\(.*\)/\1/w 111-a
}" -e "r 111"|sed -n \
-e "3,10{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-b
}" -e "3,10r 111"|sed -n \
-e "3,10{s/.\{46\}//w 111-c
}" -e "3,10r 111"|sed -n \
-e "11,18{s/\(.\{23\}\)\(.*\)/\1/w 111-d
}" -e "11,18r 111"|sed -n \
-e "11,18{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-e
}" -e "11,18r 111"|sed -n \
-e "11,18{s/.\{46\}//w 111-f
}" -e "11,18r 111"|sed -n \
-e "19,26{s/\(.\{23\}\)\(.*\)/\1/w 111-g
}" -e "19,26r 111"|sed -n \
-e "19,26{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-h
}" -e "19,26r 111"|sed -n \
-e "19,26{s/.\{46\}//w 111-i
}" -e "19,26r 111"|sed -n \
-e "27,34{s/\(.\{23\}\)\(.*\)/\1/w 111-j
}" -e "27,34r 111"|sed -n \
-e "27,34{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-k
}" -e "27,34r 111"|sed -n \
-e "27,34{s/.\{46\}//w 111-l
}" -e "27,34r 111"|sed '2,$d' \
|sed -e "r 111-a" -e "r 111-b" -e "r 111-c" \
-e "r 111-d" -e "r 111-e" -e "r 111-f" \
-e "r 111-g" -e "r 111-h" -e "r 111-i" \
-e "r 111-j" -e "r 111-k" -e "r 111-l" \
|sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/
/Jan/,/Feb/s/$/ jan /;
/Feb/,/Mar/s/$/ feb /;
/Mar/,/Apr/s/$/ mar /;
/Apr/,/May/s/$/ apr /;
/May/,/Jun/s/$/ may /;
/Jun/,/Jul/s/$/ jun /;
/Jul/,/Aug/s/$/ jul /;
/Aug/,/Sep/s/$/ aug /;
/Sep/,/Oct/s/$/ sep /;
/Oct/,/Nov/s/$/ oct /;
/Nov/,/Dec/s/$/ nov /;
/Dec/,$s/$/ dec /;/@/d' \
|sed -n "/ $1 .*$2/,/ $3 .*$4/{
/ 1 /d;p;}"|sed -n '$=';
rm 111 111-[a-l]There's only the problem that because of `-n`, it doesn't output anything.
test $# -eq 4||exec echo usage: $0 7 jul 20 aug;
x=$(echo $2 $4|sed -n '/[A-Z]/p');
if test "$x";then echo use lowercase;exit;fi
cal -y \
|sed -n -e "w 111" \
-e "3,10{s/\(.\{23\}\)\(.*\)/\1/w 111-a
}" -e "r 111"|sed -n \
-e "3,10{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-b
}" -e "3,10r 111"|sed -n \
-e "3,10{s/.\{46\}//w 111-c
}" -e "3,10r 111"|sed -n \
-e "11,18{s/\(.\{23\}\)\(.*\)/\1/w 111-d
}" -e "11,18r 111"|sed -n \
-e "11,18{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-e
}" -e "11,18r 111"|sed -n \
-e "11,18{s/.\{46\}//w 111-f
}" -e "11,18r 111"|sed -n \
-e "19,26{s/\(.\{23\}\)\(.*\)/\1/w 111-g
}" -e "19,26r 111"|sed -n \
-e "19,26{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-h
}" -e "19,26r 111"|sed -n \
-e "19,26{s/.\{46\}//w 111-i
}" -e "19,26r 111"|sed -n \
-e "27,34{s/\(.\{23\}\)\(.*\)/\1/w 111-j
}" -e "27,34r 111"|sed -n \
-e "27,34{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-k
}" -e "27,34r 111"|sed -n \
-e "27,34{s/.\{46\}//w 111-l
}" -e "27,34r 111"|sed '2,$d' \
|sed -e "r 111-a" -e "r 111-b" -e "r 111-c" \
-e "r 111-d" -e "r 111-e" -e "r 111-f" \
-e "r 111-g" -e "r 111-h" -e "r 111-i" \
-e "r 111-j" -e "r 111-k" -e "r 111-l" \
|sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/
/Jan/,/Feb/s/$/ jan /;
/Feb/,/Mar/s/$/ feb /;
/Mar/,/Apr/s/$/ mar /;
/Apr/,/May/s/$/ apr /;
/May/,/Jun/s/$/ may /;
/Jun/,/Jul/s/$/ jun /;
/Jul/,/Aug/s/$/ jul /;
/Aug/,/Sep/s/$/ aug /;
/Sep/,/Oct/s/$/ sep /;
/Oct/,/Nov/s/$/ oct /;
/Nov/,/Dec/s/$/ nov /;
/Dec/,$s/$/ dec /;/@/d' \
|sed -n -e "/ $1 .*$2/,/ $3 .*$4/{
w /dev/stderr
/ 1 /d;p;}"|sed -n '$='
or to output the calendar dates to standard out, add another temp file, 111-z test $# -eq 4||exec echo usage: $0 7 jul 20 aug;
x=$(echo $2 $4|sed -n '/[A-Z]/p');
if test "$x";then echo use lowercase;exit;fi
cal -y \
|sed -n -e "w 111" \
-e "3,10{s/\(.\{23\}\)\(.*\)/\1/w 111-a
}" -e "r 111"|sed -n \
-e "3,10{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-b
}" -e "3,10r 111"|sed -n \
-e "3,10{s/.\{46\}//w 111-c
}" -e "3,10r 111"|sed -n \
-e "11,18{s/\(.\{23\}\)\(.*\)/\1/w 111-d
}" -e "11,18r 111"|sed -n \
-e "11,18{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-e
}" -e "11,18r 111"|sed -n \
-e "11,18{s/.\{46\}//w 111-f
}" -e "11,18r 111"|sed -n \
-e "19,26{s/\(.\{23\}\)\(.*\)/\1/w 111-g
}" -e "19,26r 111"|sed -n \
-e "19,26{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-h
}" -e "19,26r 111"|sed -n \
-e "19,26{s/.\{46\}//w 111-i
}" -e "19,26r 111"|sed -n \
-e "27,34{s/\(.\{23\}\)\(.*\)/\1/w 111-j
}" -e "27,34r 111"|sed -n \
-e "27,34{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-k
}" -e "27,34r 111"|sed -n \
-e "27,34{s/.\{46\}//w 111-l
}" -e "27,34r 111"|sed '2,$d' \
|sed -e "r 111-a" -e "r 111-b" -e "r 111-c" \
-e "r 111-d" -e "r 111-e" -e "r 111-f" \
-e "r 111-g" -e "r 111-h" -e "r 111-i" \
-e "r 111-j" -e "r 111-k" -e "r 111-l" \
|sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/
/Jan/,/Feb/s/$/ jan /;
/Feb/,/Mar/s/$/ feb /;
/Mar/,/Apr/s/$/ mar /;
/Apr/,/May/s/$/ apr /;
/May/,/Jun/s/$/ may /;
/Jun/,/Jul/s/$/ jun /;
/Jul/,/Aug/s/$/ jul /;
/Aug/,/Sep/s/$/ aug /;
/Sep/,/Oct/s/$/ sep /;
/Oct/,/Nov/s/$/ oct /;
/Nov/,/Dec/s/$/ nov /;
/Dec/,$s/$/ dec /;/@/d' \
|sed -n -e "/ $1 .*$2/,/ $3 .*$4/{
w 111-z
/ 1 /d;p;}"|sed -n -e '$=' -e "1r 111-z"Use ncal because it provides the week numbers and can start weeks on Monday (personal preference). Pipe to sed, fold, and then to Perl. (Perl is awesome.)
ncal -w -3 -y -b -h -M | sed 's/ /*/g' | fold -w 27 |
tail -n 99 | perl -w -e 'use strict;
my (@g1,@g2,@g3,@aAccum)=((),(),(),()); my $tog=1;
sub clearQ{ (@g1,@g2,@g3)=((),(),());}
sub doAccum{ @aAccum=(@g1,@g2,@g3); clearQ();
print @aAccum; @aAccum=(); }
for my $wk(<>){
my $LN=$wk; chomp($LN);
if( length($LN)==0 ){ doAccum(); next;}
if($tog==1){push @g1,$wk;}
elsif($tog==2){push @g2,$wk;}
elsif($tog==3){push @g3,$wk;} $tog++;
if($tog>3){ $tog=1;}
}
doAccum();'Use the cal that is in util-linux: https://www.kernel.org/pub/linux/utils/util-linux/
cal -m starts weeks on Monday.
cal -w displays week numbers.
Personal preference is not to install Perl unless needed, e.g., for compiling OpenSSL. Takes up precious space that can be used for other things.
I understand your preference for a lean file system. In this challenge, I needed more than awk (because of arrays) and wanted an explicit solution that works regardless of calendar language.
#!/bin/sh
# trim left space triple ' '*
lt() { s="$1"; while [ ! "${s## }" = "$s" ]; do s="${s## }"; done; printf "%s" "$s"; }
# trim right space ' '*
rt() { s="$1"; while [ ! "${s%% }" = "$s" ]; do s="${s%% }"; done; printf "%s" "$s"; }
# count months for first/last special treatment
n=1
# usage: reform pos line1 line2 ... line7
reform() {
o=$1; m="$(printf "%s" $(expr substr "$2" $o 20))"; l1="$(expr substr "$3" $o 20)"
[ $n -ne 1 ] && l1="$(lt "$l1")" # suffix of spliced week
shift 3
printf "%s %s\n" "$l1" "$m"
while [ "$#" -gt 0 ]; do
l="$(expr substr "$1" $o 20 )"; lx="$(rt "$l")"; shift
if [ -z "$lx" ]; then
:
elif [ "$l" = "$lx" -o $n -eq 12 ]; then
printf "%s\n" "$lx" # whole week
elif [ -n "$lx" ]; then
printf "%s " "$lx" # prefix of spliced week
fi
done
n=$(( $n + 1 ))
}
# buffer 8 lines into arg vector via recursion hack
# then replace lines 3x to project out 3 months in this row
buffer() {
if [ "$#" -eq 7 ]; then
reform 1 "$@"; reform 24 "$@"; reform 47 "$@"
else
IFS= read line || return 1
[ "$#" -eq 0 ] && read discard
buffer "$@" "$line"
fi
}
# strip preamble and process 4 rows of months
process() { read discard; read discard; for i in 1 2 3 4; do buffer; done; }
cal -y ${1:-2021} | processI've just gotten started! I think it's comfortable in the sense that I won't need to copy and paste stuff to and from the terminal and can just code from the same file and see how the result changes. That's really neat!
I imagine I'll be feeling restricted at some point, since I'm not doing custom HTML for each post, and I want to keep the org file readable on its own, so no excessive markup.
> I like the theme you built.
Thanks, though it's really almost no styling. The syntax highlighting is done by the ruby library `rouge`. The colors were just black on white, but the Dark Reader Firefox extension I have installed would change that to these off-black and off-white colors, and I liked it, so I set them in the CSS.
LANG=c cal -y|grep '[a-z]'|egrep -v 'Su ' | xargs
or
cal -y | sed -E '1,2d;s/(.{20}) /\1\n/g;' | python3 -c “from calendar import month_name;import sys;[print(ln.strip()) for ln in sys.stdin.readlines() if ln.strip() in month_name]”
cal -y | sed -E '1,2d;s/(.{20}) /\1\n/g;' | python3 -c "from calendar import month_name;import sys;[print(ln.strip()) for ln in sys.stdin.readlines() if ln.strip() in month_name and len(ln.strip())]"
https://tio.run/##TY1BDsIgFAWv8tJNIbGtrUvWnsIag4VaEvohwKZRz4...
The point was a sed challenge. Challenges are fun sometimes.
It was also a good exercise for doing things with sed. There are times when sed is the most terse way to shape text to how you want it. It's also very portable.
easier to do:
python3 -c “import calendar;[print(m) for m in calendar.month_name]”
Yes, but not Python. azalemeth was wondering if Python could be more terse than sed for this type of work. I think Python tends to be the most verbose among all languages discussed for text handling.
> There's basically no advantage that awk has over Ruby
There are a few. The main advantage of awk over ruby for me is that it's more terse for some simple jobs. That's because of features like
- how unused variables can be used as numbers, strings, or associative arrays without previous declaration. For example, in the common snippet `awk '!a[$0]++'`, `a` is referenced as an array without having to declare it as such first, and the returned value is used as a number (i.e. by `++`) without having to declare that as such.
- how number strings can be implicitly used as numbers
- how records are split by default in a very useful way, with each field made available in a very terse manner with the `$` unary operator.
- how plain regexes can be used as booleans that match over the line.
- how simply passing a boolean expression means to print records that match. It also helps that records can be multi-line or be defined by regexes.
- how ranges can be specified with a couple of boolean expressions
- how files and co-processes can be implicitly opened on first use
It also has advantage in that
- It launches much quicker in comparison to most languages. I recently did a small shell DSL using many piped calls to awk to write some extensions for i3, and performance was decent, surprisingly. jq calls were a far bigger bottleneck.
- It's available on even really old systems. It can be refreshing at times when your only other options are sh, C, or 4GL.
There are many cases where awk is more convenient than ruby.