日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です。微力ながら共著させていただいた「シェル・ワンライナー160本ノック」をよろしくお願い申し上げます。

第22回シェル芸勉強会へ遠隔参加

世間はゴールデンウィークですがいかがお過ごしでしょうか?そんなの関係なくやること満載な上に、第22シェル芸勉強会の福岡サテライト会場を開催させていただき、更にフラフラになっております。参加者は途中帰宅した方などを含めると8人でした。

問題作成と解説の上田さん、ありがとうございます。参加者の皆様お疲れ様でした。

勉強会の情報

勉強会主催者の上田さんが公開されているページをご覧ください。 b.ueda.tech

イントロ

シェル芸はコンピュータに頑張ってもらうためのスキルかも?ってことで少し話しました。

speakerdeck.com

午前の部

福岡サテライトでは比較的初心者の方が多い?と感じていたため、午前の部は私が準備した入門編的な内容について話をしてみました。 内容は「シェルで繰り返し処理」についてです。制御構造やxargs、コマンド列作成してシェルへ食わせる、フィルタコマンド、という流れで話を進めてみました。お題があまり良くなかったかもしれないです。

speakerdeck.com

昼は皆さんで外に食事に出かけ、久しぶりに一風堂でラーメンを食らいました。

午後の部

今回もなかなか歯ごたえがある内容でした。

各福岡サテライトで私なりに解説した内容や解答例を載せておきます。

Q1

中央値を出すのはどうするか?まあ真ん中のデータを持って来ればいいのですが。 あっさりとは解けなかったので出題者上田さんの解答例を参考にさせていただきました。 awkを知らない方がいらっしゃったので、まずはデータを並び替えて横にするところまでのやり方を説明しました。

$ cat Q1/a | sort | xargs
1 1 2 3 4 6 6 8

残りはawkの$1やNRやNFについて説明しながら、出題者上田さんの解答例を解説しました。

Q2

完全にキブアップでした。カレーライスと醤油ラーメンをそれぞれ別の行にして、awkNR==1NR==2の時にゴニョゴニョするとかかなあと思ったが、敗北。

$ echo カレーライス 醤油ラーメン | tr ' ' '\n'
カレーライス
醤油ラーメン

カレーライスを縦に並べて、左に空白を入れるにはどうすればいいのか?という質問が出たので、こんな感じかなと解説しました。 grep -oオプションが驚きだったようです。

$ echo カレーライス | grep -o . | sed 's/^/      /'
      カ
      レ
      ー
      ラ
      イ
      ス

Q3

ようやく太刀打ち出来そうな問題。Tukubaiコマンドを解禁しました。行番号を右側に付けて、第一フィールドを優先キーにしてsortします。

$ cat Q3 | awk '{print $0,NR}' | sort -k1,1 -k2,2n
aaabbb 1
aaabbb 3
aaabbb 4
bababa 2
bababa 5
bbbbba 6

あとはTukubaiのyarrコマンドを使って完成です。

$ cat Q3 | awk '{print $0,NR}' | sort -k1,1 -k2,2n | yarr num=1
aaabbb 1 3 4
bababa 2 5
bbbbba 6

Tukubaiコマンドを使わない場合は、awkの連想配列を使います。

$ cat Q3 | awk '{a[$1]=a[$1]" "NR}END{for(v in a)print v,a[v]}'
bababa  2 5
aaabbb  1 3 4
bbbbba  6

逆に復元するにはこんな感じですね。Tukubaiコマンド使っています。

$ cat Q3.ans | tr -s ' ' | tarr num=1 | sort -k2,2n | awk '{print $1}'
aaabbb
bababa
aaabbb
aaabbb
bababa
bbbbba

Q4

なんとか解けました・・・考え方は、実行するコマンド列を作ってシェルに食わせて実行する方法です。 まずは素数の列挙。素数の列挙方法も解説した。

$ seq 10 | gfactor | awk 'NF==2{print $2}'
2
3
5
7

列挙した素数からsedの命令コマンドを作成します。

$ seq 10 | gfactor | awk 'NF==2{print $2}' | tr '\n' 'p' | sed 's/p/p;/g' | sed "s/.*/\'&\'/"
'2p;3p;5p;7p;'

さらに実行するコマンド列を作成します。

$ seq 10 | gfactor | awk 'NF==2{print $2}' | tr '\n' 'p' | sed 's/p/p;/g' | sed "s/.*/\'&\'/" | sed 's/^/sed -n /' | sed 's/$/ Q4/'
sed -n '2p;3p;5p;7p;' Q4

あとはコマンド列をシェルに食わせて実行します。

$ seq 10 | gfactor | awk 'NF==2{print $2}' | tr '\n' 'p' | sed 's/p/p;/g' | sed "s/.*/\'&\'/" | sed 's/^/sed -n /' | sed 's/$/ Q4/' | sh
りんご
みかん
りんご
りんご

更にそこからsort | uniq -cの定番処理で集計して完成です。

$ seq 10 | gfactor | awk 'NF==2{print $2}' | tr '\n' 'p' | sed 's/p/p;/g' | sed "s/.*/\'&\'/" | sed 's/^/sed -n /' | sed 's/$/ Q4/' | sh | sort | uniq -c
   1 みかん
   3 りんご

Q4まで終わって一旦休憩です。既にこの時点でノーミソオーバーヒート状態です。

Q5

さて午後の部後半開始です。いきなり難問きました。

とりあえず1から12までの組み合わせを出してみるかと考えました。組み合わせ出すにはbashのブレース展開でしょうか?

まずは実行するコマンド列を作成します。

$ seq -f '{,%g}' 1 12 | tr -d '\n' | sed 's/^/echo /' 
echo {,1}{,2}{,3}{,4}{,5}{,6}{,7}{,8}{,9}{,10}{,11}{,12}

コマンド列をシェルに食わせて組み合わせを出力します。

$ seq -f '{,%g}' 1 12 | tr -d '\n' | sed 's/^/echo /' | bash | tr ' ' '\n' | head
12
11
1112
10
1012
....
1234567891112
12345678910
1234567891012
1234567891011
123456789101112

ただ残念ながらこれだとダメですね。本当は各数値の間に空白で区切りを入れたいのですがギブアップです。

Q6

何とか解けました。これはsedの置換コマンドを作成すればいいですね。

まずはsedの置換コマンド文字列を作成します。

$ cat Q6_2 | sed 's;^;s/;' | sed 's;$;/;' | tr ' ' '/'
s/X/駄馬の首/
s/Y/人間/
s/Z/死相/

この置換コマンド文字列を、GNU sedの-f -オプションで標準入力から食わせて実行すれば完成です。

$ cat Q6_2 | sed 's;^;s/;' | sed 's;$;/;' | tr ' ' '/' | gsed -f - Q6_1
出力は省略

Q7

多分UNIXのお勉強な問題。bashをkill -9したりすればええんじゃ、という話をしたりしました。 出題者上田さんの解答では、execを使った例が出たりしました。

Q8

最後はまたちょっと難問でした。

要するに関数名を抜き出せばいいのでは?ということで下記のように抜き出してみました。

$ cat Q8.cc | grep '^[a-z]' | grep -vE '(using|main)'
void aho(void)
string nazo(void)

後はこれを挿入すればいいのでしょうが、やり方が分からずタイムアップでした。

今回も難しかったですが、3問解けたのでよしとします。 福岡サテライト参加者の方々への解説をどうするかで頭フル回転でした。 解けない問題は重要なポイントの解説を重視するスタイルで福岡サテライトは続けてきました。 午前の部の初心者向け内容も大事な気がしてきました。 終了後、Personal Tukubaiとかちょっとした話をしたりしました。