たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

第49回シェル芸勉強会に参加しました

前回に続きリモートシェル芸勉強会でした。今回は問題は簡単だけど解答を変態にしなさいという感じらしい。変態じゃないのでわからんね。

問題と解答はこちら。当日のライブストリームはこちら

今回もbash/zshPowerShellの解答を書いていきます。PowerShellの方は変態度があまり高くないです。

Q1

「あ」と出力する問題。簡単にいくなら echo あとするわけですが、これでは変態度が低い。らしい。

$ seq 3000000 | factor | awk 'NF==2' | awk 'NR==1||NR==2||NR==182043{print $2}' | paste -sd '*' | sed 'iobase=16' | bc | sed 's/./\L&/g' | xxd -ps -r

はい。最中は提出できなかった・・・(凝りすぎた)

$ echo -n あ | xxd --ps | xargs -I@ printf "%d
" 0x@
14909826

「あ」を10進数で表すと14909826なので、これを素因数分解します。

$ echo -n あ | xxd --ps | xargs -I@ printf "%d
" 0x@ | factor
14909826: 2 3 2484971

これらの数値をseq | factorから取り出して行きます。

$ seq 3000000 | factor | awk 'NF==2' | grep -nP "^(2484971|2|3):"
1:2: 2
2:3: 3
182043:2484971: 2484971

1と2と182043行目に欲しい数字があるので、これを取り出します。

$ seq 3000000 | factor | awk 'NF==2' | awk 'NR==1||NR==2||NR==182043{print $2}'
2
3
2484971

これを*で連結して、評価してxxdで元に戻します。

PowerShellでの解答も行きます

@(2,3,29,71) | ForEach-Object { $agg=1 } { $agg*=$_ } { [char]$agg }
あ

まぁこれもやってること同じです。

Q2

ls -lを使わず、ファイル + タイムスタンプを表示する問題。ls -l が使いたい。 これはstatを使えばいいということですねえ

$ mkdir aaaa && cd aaaa && touch {a,b,c,d}
$ git init && git status -s | awk '{print $NF}' | xargs -I@ bash -c 'echo @ $(stat -c %y @)'

ファイルのリストアップにgit statusを使ったのですが、普通にecho *とかでもよかったですね。

PowerShellでの解答も行きます。

$ gi ./* |%{ $_.Name +" "+ $_.LastWriteTime  }

giGet-Itemへのエイリアスです。パイプで渡ってきたオブジェクトからプロパティを取り出しているだけですね。

Q3

seq 30からはじめて、3のつく数値だけを出力する問題。いつもはgrep 3で終わるけれど、これは変態度が低いんですね。慣れてきました。

$ seq 30 | perl -nle 'print if /3/'
$ seq 30 | csharp -e 'var x="";while((x=Console.ReadLine()) != null) if(x.Contains("3")) Console.WriteLine(x);'
3
13
23
30

解説するまでもないですね。どちらも愚直な実装だと思います

PowerShellでの解答も行きます。こっちもSelect-String 3とするだけなんですが、変態度がどうのこうの。

$ 1..30 | ?{[System.Text.RegularExpressions.Regex]::new("3").Match($_).Success}
3
13
23
30

?Where-Objectへのエイリアスです。ブロックを評価してtrueなものだけを通します。[System.Text.RegularExpressions.Regex]正規表現を扱うクラスです。これの正規表現な文字列を与えて、Matchメソッドで$_を評価していきます。今回は単に3と渡しているので3が含まれているとマッチするんですね。

Q4

echo 3+4+5+6からはじめて式を評価する問題。普通にやるならbcに渡せばいいですね。

$ echo 3+4+5+6 | bc
18

でもこれは変態的でないので、なんとか変態的にしていきます

$ echo 3+4+5+6 | tr + \n | jq -s add
18

trを使って+を改行に置換し、jqで足し算します。このjqでの足し算好きなのでよく使います。

PowerShellでの解答も行きます

$ echo 3+4+5+6 | iex
18

iexInvoke-Expressionへのエイリアスです。渡された文字列を評価します。まぁevalだと思っていいと思います。PowerShellからみると普通ですね。ちょっとjq的な答えも考えてみます。

$ (echo 3+4+5+6) -split '\+' | measure -sum | %{$_.Sum}
18

measureMeasure-Objectへのエイリアスです。パイプラインで渡ってきたりしてものをなんかいろいろ数え上げてくれたりします。オプションなしだと個数だけを数えますが、-sumを付けると、総和を計算してくれます。

Q5

echo てぶくろからはじめてろくぶてを出力する問題。普通はrevすればいいですね

$ echo てぶくろ | rev
ろくぶて

でもこれは変態的でないので、なんとか変態的にしていきます

$ echo てぶくろ | grep -o . | sed 's/./"&"/' | paste -sd , | sed -E 's/.+/[&]/' | jq reverse --indent 0 | tr -d ',"[]'
ろくぶて

jqreverseを使って反転させることにしました。jqJSONを扱うツールなのでてぶくろJSONの配列に加工します。

$ echo てぶくろ | grep -o . | sed 's/./"&"/' | paste -sd , | sed -E 's/.+/[&]/'
["","","",""]

これをjq reverseで反転させます

$ echo てぶくろ | grep -o . | sed 's/./"&"/' | paste -sd , | sed -E 's/.+/[&]/' | jq reverse --indent 0
["","","",""]

いらないところをtrで削って終わりですね。

PowerShellでの解答も行きます・・・が、まぁこれは普通だな・・・

$ echo てぶくろ | %{[System.Linq.Enumerable]::Reverse($_) -join ""}
ろくぶて

普通過ぎるので別の方法をとります。せっかくPowerShellでは.NETのクラスが使えるので、それを使ってみたいと思います。

$ (echo てぶくろ) -split "" | % {$st=New-Object System.Collections.Stack} {$st.Push($_)} {$st -join ""}
ろくぶて

System.Collections.Stackです。説明するまでもないですがStackを扱うオブジェクトです。Stackに順番に入れて取り出せば逆順になりますね。

Q6

数字を使わず素数を10個ほど出力する問題。数字を使うなら seq inf | factor | awk 'NF==2{print $NF}' | headこんな感じですね

c++を使ったりしてエラトステネスを実装しようかと思ったんですが、単純に2 3 5 7みたいな感じで出力すればいいんだなあってなりました。もちろん数字のところは難読化とかが必要ですが。頭…柔らかくしていこうな!

Q7

数字を使わずに0~9を出力する問題。普通なら seq 0 9でいいですね

$ seq $? $((-~-~-~-~-~-~-~-~($$/$$)))
0
1
2
3
4
5
6
7
8
9

$?は前のコマンドの終了値です。前のコマンドが正常終了していれば$?は0になります。$((-~-~-~-~-~-~-~-~($$/$$)))はややこしく見えますが、単項演算子を連続でつけているだけです。-は負に、~はビット反転です。$$/$$のうち$$はプロセス番号です。プロセス番号をプロセス番号で割ると1なのでこの式は-~-~-~-~-~-~-~-~1を評価することになります。この結果は9になるので、seq 0 9が評価されますね

別解も用意しました。これは前回の勉強会で出た問題の再利用です。詳しくは説明しませんが、スクリプト再帰している感じですね。

$ echo 'echo $((SHLVL-$$/$$)); bash -c $'$? > x && chmod +x x && ./x | head

PowerShellでの解答も行きます

$ [int]!$?..("やっほーうんこだよ").Length
0
1
2
3
4
5
6
7
8
9

やっほーうんこです。せっかくPowerShellなので、PowerShellにあるものでやってみました。

$ 0..9
0
1
2
3
4
5
6
7
8
9

これで範囲内の数値を出力できます。こいつを数字を使わずにやります。まず0は[int]!$?です。$?bash/zshなどとおなじで前のコマンドの終了ステータスが入っています。正常ならTrueが入っているので、!Falseにしてintにキャストすると0になります。

9のほうは計算で出してもいいんですが、せっかくオブジェクトにメソッドが生えているので使いたいです。ちょうど「やっほーうんこだよ」が9文字なのでこれを使いました。Lengthで長さを取得できます。めでたく9が得られました。

Q8

unkoファイルは以下のようになっています

んこうんこううんうこうんんこうこんんこう
んこここんこうこうんこうこうんんこうんこ
うこううんうここうこんんんうんううんんこ
んんんこここううううんこんこんうこうこう
んこうんんんううこううううこうここうんん
んここうこんうここうんううんこうんこうう
うこうんここここんんうこここここここんこ
うこうこここうんんううううんここうこんう
ここうんうんんんんこうこんううんんこんう
んうううここんここここううこんんんうんう
んこうんんこんううんうんこんんうんこんん
んんうここうこううこううんんうこうこうん
こううこううここんんこんううんうんんんう
こうこうんんんこんこんんううんんんこんこ
んんんこうんうこうここうんこここうんこん

ここからうんこが含まれる行番号だけを出力する問題です。ただし、ワンライナー中に日本語を使ってはいけないとのこと。今回はうんこないと思ったけどやはり

ちなみにどんな感じにうんこが分布しているかはこんな感じ

$ cat unko | grep -n うんこ
1:んこうんこううんうこうんんこうこんんこう
2:んこここんこうこうんこうこうんんこうんこ
4:んんんこここううううんこんこんうこうこう
6:んここうこんうここうんううんこうんこうう
7:うこうんここここんんうこここここここんこ
8:うこうこここうんんううううんここうこんう
11:んこうんんこんううんうんこんんうんこんん
15:んんんこうんうこうここうんこここうんこん

でも「うんこ」を使っているのでダメですね。

$ cat unko | awk "/$(uconv -x hira <<< unko)/{print NR}"
1
2
4
6
7
8
11
15

uconv -x hira <<< unkounkoをひらがな読みに変換します。これはまさかなんですがちゃんと「うんこ」とでます。うれしいですね。というわけでこのワンライナーは以下のようになります

$ cat unko | awk '/うんこ/{print NR}'

はい。

PowerShellでの解答も行きます

$ cat unko | sls ((@(58,135,71) |%{[char]($_+12300)}) -join "") | %{$_.LineNumber}
1
2
4
6
7
8
11
15

うむ・・・。なんか面白い文字の難読化を見つけたいですね

LT

ocsというopyのC#版みたいなのを用意していたんですが、いろいろ事故ってLTできませんでした。モノ自体は完成しているので、そのうちブログ書きますね

終わりに

今回は問題としては簡単だったのか、いろんな解答が見られてとても面白かったです。ただなんかやっぱり疲労感は感じますね。次回も楽しみです!!!開催ありがとうございました!!!