色々と忙しい状況ではあったが、第25回シェル芸勉強会の福岡サテライト会場が開催出来た。開始時間が早くなったが会場はベータソフト様会議室を準備していただいた。大変感謝である。今回の参加者は9名だった。山を越えた県外からの参加者もいらっしゃった。朝から夕方までかなり喋ったので喉がガラガラになったのであった。
イベント案内ページ
本家東京会場
大阪サテライト
福岡サテライト
大阪サテライトの開催レポートはこちら。今回もLTがすごいことに。
開始前
まずはみんなで自己紹介をした後、下記のスライドでイントロのお話をした。先日お仕事用の液晶ディスプレイを新調したばかりなのだが、1週間後に新機種が発表されて複雑な気持ちが否めない今日この頃。
午前の部
気を取り直して午前の部を開始した。今回は初心者の方が多いというのもあり、福岡サテライトは独自に進めることにした。Apache HTTP Severのログファイルを扱いながら、awk
、grep
、cut
、sort
、uniq
などの使い方を説明。簡単な集計の方法へと話を進めるスタイルを試した。ネタとしては下記のページを参考にさせていただいた。うまく伝わっただろうか??
Apacheのログファイルは、上田会長のページで公開されているシェル芸勉強用データを利用させていただいた。
お昼は餃子の王将へ食事に出かけた。大量の餃子もみんなで喰らえば怖くない!!というスタンス。
午後の部
午後からはいよいよ本番だ。今回の問題はパズル系だったが、難易度は幅広く内容も重要なことが多かった気がする。 問題と解答のページは、上田さんブログの記事を参照してもらいたい。
【問題と解答】第25回もう4年もやってんのかシェル芸勉強会 – 上田ブログ
ではここからは福岡サテライト会場でで考えた解答と説明の例をあげていく。問題が解けることよりも解説と考え方の説明を重視。解答例はMacで作成。GNU版のコマンドはguniq
など頭にgが付くコマンドになっている場合がある。
Q1
dig
やping
などのコマンド出力を文字列処理。grep
のオプションやawk
、tr
の使い方な問題。
$ dig www.usptomo.com | grep -A1 ';; ANSWER SECTION:' | grep -v ';; ANSWER SECTION:' | awk '{print $NF}' 157.7.203.188 $ ping -c 1 www.usptomo.com | head -n 1 | awk '{print $3}' | tr -dc '0-9.' 157.7.203.188
Q2
awk
のNR
を利用して階段状の出力を作ってみる。
$ yes ひらけ!ポンキッキ | head -n 9 | awk '{for(i=1;i<NR;i++){printf "#"}printf $0$0;print ""}' ひらけ!ポンキッキひらけ!ポンキッキ #ひらけ!ポンキッキひらけ!ポンキッキ ##ひらけ!ポンキッキひらけ!ポンキッキ ###ひらけ!ポンキッキひらけ!ポンキッキ ####ひらけ!ポンキッキひらけ!ポンキッキ #####ひらけ!ポンキッキひらけ!ポンキッキ ######ひらけ!ポンキッキひらけ!ポンキッキ #######ひらけ!ポンキッキひらけ!ポンキッキ ########ひらけ!ポンキッキひらけ!ポンキッキ
後は上下ひっくり返し、固定長で文字列を切り出して完成。
$ yes ひらけ!ポンキッキ | head -n 9 | awk '{for(i=1;i<NR;i++){printf "#"}printf $0$0;print ""}' | tail -r | cut -c 9-17 ひらけ!ポンキッキ らけ!ポンキッキひ け!ポンキッキひら !ポンキッキひらけ ポンキッキひらけ! ンキッキひらけ!ポ キッキひらけ!ポン ッキひらけ!ポンキ キひらけ!ポンキッ
Linuxの場合は最後の2つのコマンドを下記のように変更しよう。
tail -r
をtac
へ変更cut -c9-17
をawk {print substr($1,9,9)}
へ変更(GNU cutコマンドはマルチバイト非対応)
###Linux用解答### $ yes ひらけ!ポンキッキ | head -n 9 | awk '{for(i=1;i<NR;i++){printf "#"}printf $0$0;print ""}' | tac | awk '{print substr($1,9,9)}'
Q3
tee
を使うことしか思いつかなかった。@ebanさんの下記の解答例が面白くて、福岡会場で取り上げて説明をした。UNIXの入出力は全てファイルとして扱うことが出来るという素敵な例。
Q3 $ grep '/bash$' /etc/passwd | cp /dev/stdin hoge#シェル芸
— eban (@eban) October 29, 2016
Q4
ギブアップ!上田会長の解答例を参考にした。
Q5
ちょっと難しく考えすぎたかもしれないが、下記の解答例を考えて解説した。まずはsleep
を使ったコマンド列を作っておく。キャリッジリターンを使って改行させない指定がポイント。
$ yes | head | awk '{for(i=1;i<=NR;i++){printf "*"}print ""}' | sed "s/^/printf '/" | sed "s/$/\\\\r'; sleep 1/" printf '*\r'; sleep 1 printf '**\r'; sleep 1 printf '***\r'; sleep 1 printf '****\r'; sleep 1 printf '*****\r'; sleep 1 printf '******\r'; sleep 1 printf '*******\r'; sleep 1 printf '********\r'; sleep 1 printf '*********\r'; sleep 1 printf '**********\r'; sleep 1
事前に上記の連続コマンドを作成し、シェルに渡して実行する例として解説した。
$ yes | head | awk '{for(i=1;i<=NR;i++){printf "*"}print ""}' | sed "s/^/printf '/" | sed "s/$/\\\\r'; sleep 1/" | sh **********
Q6
内容はおそらく16進数ダンプだと思いそのままデコード、file
コマンドに渡したが謎。
$ echo b730a730eb30b8820a00 | xxd -p -r | file - /dev/stdin: Non-ISO extended-ASCII text
最後をよく見ると0a00
とある。0a
は改行コードの16進ダンプ。改行が最後になってないので不自然である。ということで2文字つまり1バイトずつ入れ替えている?と推測。file
コマンドで確認して見たがよくわからず。2文字ずつ区切って入れ替える処理や、file
コマンドでの確認について詳しく解説をした。後は会長の解答通りnkf
コマンドで。
$ echo b730a730eb30b8820a00 | fold -w 2 | xargs -n 2 | awk '{print $2,$1}' | xargs | tr -d ' ' | xxd -p -r | file - /dev/stdin: data
Q7
秒毎に繰り返しを実行するやり方を考えてもらった。date
コマンドはGNU版を利用。最初に解説した解答例は下記の通りで、date
コマンドを86400回繰り返すやり方。
$ seq 0 86399 | while read n; do gdate -d "2016-10-29 00:00:00 $n sec" '+%s'; done $ for n in {0..86399}; do gdate -d "2016-10-29 00:00:00 $n sec" '+%s'; done $ seq 0 86399 | xargs -I@ gdate -d '2016-10-29 00:00:00 @ sec' '+%s' $ seq 0 86399 | sed "s/^/gdate -d '2016-10-29 00:00:00 /" | sed "s/$/ sec' '+%s'/" | sh
次にdate
コマンドのフィルタモードで高速処理する方法を説明した。
$ seq 0 86399 | sed -e "s/^/2016-10-29 00:00:00 /" -e "s/$/ sec/" | gdate -f - '+%s'
素数のみを出力するのは、いつものシェル芸的方法で。あ、最後にUNIX時間から元に戻すのを忘れていた。
$ seq 0 86399 | sed -e "s/^/2016-10-29 00:00:00 /" -e "s/$/ sec/" | gdate -f - '+%s' | gfactor | awk 'NF==2{print $2}'
Q8
福岡では難易度と時間の都合上省いた。後で考えた解答がこちら。上田会長の解答例を少し変えて、後半の行列入替をawkで頑張っただけ。
$ seq 1 50 | awk '{a=sin($1/3) * 10 + 10;for(i=0;i<a;i++)printf "-";printf "+";for(i=a;i<20;i++)printf "-";print ""}' | awk '{for(i=1;i<=NF;i++)a[NR,i]=$i}END{for(j=1;j<=NF;j++){for(i=1;i<=NR;i++){printf a[i,j]}print ""}}' FS= | tail -r | tr '+-' '* ' *** ** ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *** ***
行列入替は下記の記事で書いたものを流用。
終了後
もう一度全体を通して復習をしながら説明した。福岡の解答例はATNDイベントページのコメントに貼り付けて共有すればいいね!ということに気がついた。
今回も解説重視でひたすらしゃべった感じだ。わかりやすい説明はどうすればいいか、毎回悩みながらのベシャリである。シェルに親しむ楽しさ、コンピュータに頑張ってもらうためのヒントなどが伝わってるといいなあ。今回は喉がかなりガラガラになったのであった。問題作成と解説の上田会長、運営や会場管理者の方々、参加者の皆様、今回もありがとうお疲れ様。