前回に続きリモートシェル芸勉強会でした。今回は問題は簡単だけど解答を変態にしなさいという感じらしい。変態じゃないのでわからんね。
問題と解答はこちら。当日のライブストリームはこちら
今回もbash/zshとPowerShellの解答を書いていきます。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 }
gi
はGet-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
iex
はInvoke-Expressionへのエイリアスです。渡された文字列を評価します。まぁeval
だと思っていいと思います。PowerShellからみると普通ですね。ちょっとjq
的な答えも考えてみます。
$ (echo 3+4+5+6) -split '\+' | measure -sum | %{$_.Sum}
18
measure
はMeasure-Object
へのエイリアスです。パイプラインで渡ってきたりしてものをなんかいろいろ数え上げてくれたりします。オプションなしだと個数だけを数えますが、-sum
を付けると、総和を計算してくれます。
Q5
echo てぶくろ
からはじめてろくぶて
を出力する問題。普通はrev
すればいいですね
$ echo てぶくろ | rev
ろくぶて
でもこれは変態的でないので、なんとか変態的にしていきます
$ echo てぶくろ | grep -o . | sed 's/./"&"/' | paste -sd , | sed -E 's/.+/[&]/' | jq reverse --indent 0 | tr -d ',"[]'
ろくぶて
jq
のreverse
を使って反転させることにしました。jq
はJSONを扱うツールなのでてぶくろ
を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 <<< unko
でunko
をひらがな読みに変換します。これはまさかなんですがちゃんと「うんこ」とでます。うれしいですね。というわけでこのワンライナーは以下のようになります
$ 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できませんでした。モノ自体は完成しているので、そのうちブログ書きますね
終わりに
今回は問題としては簡単だったのか、いろんな解答が見られてとても面白かったです。ただなんかやっぱり疲労感は感じますね。次回も楽しみです!!!開催ありがとうございました!!!