日々之迷歩

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

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

「第37回シェル芸勉強会:福岡サテライト」レポート

シェル芸勉強会福岡サテライトの開催が、昨年の冬以降途絶えていた。時間が取れなかったり、前回は大雨の影響で中止したり。株式会社レスコ様の会議室を貸していただき、久しぶりに開催出来た。いつもながら会議室担当者の方には感謝。今回は述べ8人の参加者が集った。そのうち新規の方が3人。そのうち2人は遠方からいらっしゃった。

久しぶりの開催でちと不安も。

twitter.com

勉強会募集サイト

本家東京会場募集

usptomo.doorkeeper.jp

大阪サテライト会場募集

atnd.org

福岡サテライト会場募集

atnd.org

イベント報告関連

(上田会長による)jus共催 第37回シェル芸勉強会リンク集

jus共催 第37回シェル芸勉強会リンク集 | 上田ブログ

(上田会長による)Togetterまとめ

togetter.com

午前の部

最初に話したイントロ。

speakerdeck.com

午前中は鳥海さんによる文字コードの話を聴く予定だったが、方針を変えて主に初参加者向けの話をすることにした。勉強会の趣旨や内容、よく使うコマンドやその使い方、調べ方など。catのオプション、sort、uniq、comm、paste、ls、findとxargsの合わせ技、bc、awkなどを一通り説明。

その後以前実施した「初心者向けシェル芸勉強会」で解いた問題をやってみることにした。

シェル芸初心者向け勉強会

papiro.hatenablog.jp

シェル芸勉強会過去問第2回(問題と解答)

シェル芸勉強会過去問第2回(問題と解答) · GitHub

解いてもらって解説をしたのは下記の問題。

問題2: 計算

以下のファイル中の数字を全部足し算してください。

$ cat Q2/num
1
2 3 4
5 6
7 8 9 10

文字列処理で数式を作ってbcへ突っ込むという解答を解説。awk NFは最後に改行を入れるためのおまじない。

$ cat Q2/num | tr '\n' ' ' | tr ' ' + | sed 's/.$//' | awk NF | bc
55

昼食

前回に新規開拓した中華料理店へ。今回も美味し。何故か福岡県や福岡市の良さやアレな事が話題になったり。福岡県と福岡市の相性ってアレだよねとか(自粛)。

午後の部

いよいよ問題にトライの時間。今回は難しくもあるがくだらない問題らしく・・・

twitter.com twitter.com

要するに「シェル芸bot映え」するような出力を目指す問題だった。シェル芸botについては、Twitterを使ってる人はフォローして使ってみて欲しい。作者の方が手動でフォロー返しされたら利用可能になるので、しばしお待ちを。

問題と解答例はこちら。(出題者の上田会長ブログ)

【問題と解答】jus共催 第37回シェル芸bot生きてるかどうか分からないけどシェル芸bot向けシェル芸勉強会 | 上田ブログ

今回はいつも以上に福岡サテライトなりのペースで、というより私なりのペースでフォローや解説を重視しながら進行した。

Q1

FizzBuzzの出力をアニメーション化する問題。福岡ではとりあえずFizzBuzzの問題を解くことにした。普通はループで剰余計算の結果でif文書くが、sedで文字列処理を使った解答を。GNU sedの0~3の様な行指定がポイント。最後のxargsは縦スペース節約。

$ seq 30 | sed '0~3s/$/Fizz/;0~5s/$/Buzz/;' | sed 's/^[0-9][0-9]*\([^0-9][0-9]*\)/\1/' | xargs
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz

Q2

福岡では下記の出力を出すことまでを目標にした。

   /\
  /  \
 /    \
/      \

上田会長の解答前半部を解説した。GNU sedのラベルを使った繰り返し処理という初心者殺しな内容だったが、とりあえず理解してもらえたようだ。繰り返しのbコマンドは無限にくり返す。

$ echo '   /\' | sed ':a p;s@ /@/  @;b a'
   /\
  /  \
 /    \
/      \
/      \
..(無限君)...

繰り返しコマンドでbの代わりにtを使うと、直前のsコマンドが失敗するまで繰り返す。

$ echo '   /\' | sed ':a p;s@ /@/  @;t a'
   /\
  /  \
 /    \
/      \
/      \

Q3

問題簡略化のため、3桁の数字で各桁を足すと15になる数字の列挙をする事にした。bashのブレース展開、sedで空白を入れる、awkで処理という流れ。

$ echo {0..9}{0..9}{0..9} | tr ' ' '\n' | sed 's/./& /g' | awk '$1+$2+$3==15' | tr -d ' ' | xargs
069 078 087 096 159 168 177 186 195 249 258 267 276 285 294 339 348 357 366 375 384 393 429 438 447 456 465 474 483 492 519 528 537 546 555 564 573 582 591 609 618 627 636 645 654 663 672 681 690 708 717 726 735 744 753 762 771 780 807 816 825 834 843 852 861 870 906 915 924 933 942 951 960

awkの-F ''オプションで空文字を区切り文字に指定すると、$1から順に1文字ずつ扱えて便利。

$ echo {0..9}{0..9}{0..9} | tr ' ' '\n' | awk -F '' '$1+$2+$3==15' | xargs
069 078 087 096 159 168 177 186 195 249 258 267 276 285 294 339 348 357 366 375 384 393 429 438 447 456 465 474 483 492 519 528 537 546 555 564 573 582 591 609 618 627 636 645 654 663 672 681 690 708 717 726 735 744 753 762 771 780 807 816 825 834 843 852 861 870 906 915 924 933 942 951 960

Q4

bash4の機能でUnicodeのコードポイントを指定した文字表示が可能という解説。macのログインシェルはbash3なので出来ない。Macの人はhomebrewで別途bash4をインストールしてもらった。

$ echo -e \\U1F000
🀀

空文字の列とブレース展開して、同じ文字を繰り返し表示してみるところまでやってみた。

$ echo -e \\U1F00{0,1,2,3,6}{,,,}
🀀 🀀 🀀 🀀 🀁 🀁 🀁 🀁 🀂 🀂 🀂 🀂 🀃 🀃 🀃 🀃 🀆 🀆 🀆 🀆

Q5

上田会長の解答例について前半部を解説。「山田」という漢字を下記の様にバナー化する出力を考える。

__@__
__@__
__@__
@_@_@
@@@@@
_____
@@@@@
@_@_@
@@@@@
@_@_@
@@@@@

上記のバナーを1行ずつ確認すると、下記の3パターンになる事が分かる。

@@@@@ (5列とも埋まる)
__@__ (3列目だけ埋まる) 
@_@_@ (1列目、3列目、5列目が埋まる)
_____ (全て埋まらない)

上記の4パターンを1列に並べて、バナー化した「山田」が出るようにawkで順番に指定して出力する。1行目から3行めは3列目だけ埋まるパターン、4列目は1列目、3列目、5列目が埋まるパターンという具合。

$ echo '@@@@@ __@__ @_@_@ _____' | awk '{print $2,$2,$2,$3,$1,$4,$1,$3,$1,$3,$1}' | xargs -n1
__@__
__@__
__@__
@_@_@
@@@@@
_____
@@@@@
@_@_@
@@@@@
@_@_@
@@@@@

Q6

時間がなかったため飛ばした。

Q7

まずはfiglettoiletコマンドについて解説。インストール出来る人はインストールしてもらった。次にmecabを使ってカタカナに変換。awkで-F , '{print $NF}'と指定すると、カンマ区切りで最後の列のみを表示することを利用。

$ echo とじ丼 | mecab -E '' | awk -F , '{print $NF}' 
トジ
ドンブリ

あとは改行を外してtoiletコマンドへ。

$ echo とじ丼 | mecab -E '' | awk -F , '{print $NF}' | tr -d '\n' | toilet
  m                           m                                m m    
   #             ""m  # #      #   # #      mm          m    mm#m"    
   #mm          mm             #mm            "    #     """"   #     
   #  ""m         "    m"      #  ""m             #            #      
   #                 m"        #                m"           m"       
   #            "mm""          #           "mm""          mm"         
                                                                      
                                                                      
       m                                                              
  "m    #                                                             
   #    #                                                             
   #    #                                                             
       #                                                              
     m"                                                               

上田会長の解答だと、漢字を平仮名にするのにkakasiを使っていた。こちらの方が簡単だったか。そういえばkakasiを使った事があんまりなかったかも。

Q8

簡略化のため下記の様な出力をする事に。1行を左右逆にして2行目に出す。

鉄皿鶏のチリソース定食
食定スーソリチの鶏皿鉄

まずは左右逆にするrevコマンドを解説。

$ echo 鉄皿鶏のチリソース定食 | rev
食定スーソリチの鶏皿鉄

次にmoreutilsに含まれるpeeコマンドを解説。上記のrevコマンドを利用して下記の通り。

$ echo 鉄皿鶏のチリソース定食 | pee cat rev
鉄皿鶏のチリソース定食
食定スーソリチの鶏皿鉄

peeコマンドの代わりに、teeコマンドとプロセス置換の組み合わせでも可能。

$ echo 鉄皿鶏のチリソース定食 | tee >(cat) >(rev) > /dev/null
鉄皿鶏のチリソース定食
食定スーソリチの鶏皿鉄

終了後

シェル芸勉強家の問題を解く時間が終わった後は、午前に引き続き初心者向けの話と「初心者向けシェル芸勉強会」でやったウォーミングアップ問題を解いてもらった。

色々と話をしたのだが、ここではコマンドライン自体もテキストデータだと思うと面白いよという話題を書く。数字の列挙から、日付を列挙するコマンド列をテキスト処理で作成。

$ seq 0 2 | sed 's/^/date -d "/' | sed 's/$/ day"/'
date -d "0 day"
date -d "1 day"
date -d "2 day"

作成したコマンド列をパイプでbashへ渡して実行。繰り返し処理が無くてもコマンドが順次実行されるよ。

$ seq 0 2 | sed 's/^/date -d "/' | sed 's/$/ day"/' | bash
2018年  9月  4日 火曜日 23:06:42 JST
2018年  9月  5日 水曜日 23:06:42 JST
2018年  9月  6日 木曜日 23:06:42 JST

最後に下記の解いてもらった。

問題: 下記の日付一覧から、月毎の日の数を数えてください

$ cat date1
20150101
20150121
20150201
20150202
20150203
20150310

ポイントは年月までの左から6文字だけを切り出す事。6文字を切り出すにはcut -c1-6awk {print substr($0,1,6)}という意見が出た。他にはgrepを使った下記も補足しておいた。

$ cat data1 | grep -o '^......'
201501
201501
201502
201502
201502
201503

後はuniq -cを使って集計すれば良い。

$ cat data1 | grep -o '^......' | uniq -c
      2 201501
      3 201502
      1 201503

更にsort -nrまでつなげて多い順に並べるのは、良く使うイデェオムだね。

$ cat data1 | grep -o '^......' | uniq -c | sort -nr
      3 201502
      2 201501
      1 201503

全体を通して

今回は2名の方が遠方からいらっしゃった。プログラミングの勉強をしていて、興味本位で参加してみたという方も。参加者の間口が少しでも広がるのは良い事だと思う。

とりあえず福岡サテライトは私のアドリブでペース配分してみよう。後は参加者の反応をもっと見たり、時間をとる必要もありそうだ。

終了後の後片付けで頼まれていた外の張り紙を外し忘れるミスをやってしまい、会場管理の方にご迷惑をおかけしてしまった。次回からは張り紙外しを忘れないようにせねば。