かしいのはてな

技術ブログ備忘録用

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

概要

シェル(主にbash)の理解と使い方を深める勉強会です。

東京を中心に福岡・大阪の2箇所のサテライト会場からはTwitterYoutubeを使ってリモート参加するという内容でした。

会場

みんなで同じ問題を解きます。(てか、ほとんど難しくてとけませんでした笑)

問題

みなさんのコメントおもしろくて笑いましたし、回答は秀悦すごい勉強になりました。

ツイッターまとめ

ライブストリームも見れるようなので、共有しておきました。

Youtube

 

福岡サテライト会場

各会場で、午前と午後で内容がわかれており、本件は午後の部となっております。

福岡サテライト会場では午後の開始の前に、午前の部は、ぱぴろん (@papiron) | Twitter さんからapacheのログファイルを通して、`awk`,`grep`,`sort`,`cut`,`uniq`などの使い方を始め、bashそのもののことを説明していただきました。

実はSoftware Design2016年12月号

の執筆に関わっている方で、期限がのこりわずかでお忙しいところ、午後の部が終了後もいろいろと説明していただきました。大変ありがとうございました!

 

そんなぱぴろん (@papiron) | Twitter さんのSlideがこちら

 

speakerdeck.com

今回のネタであるapacheのログデータは、この会主催の上田さんのブログで公開されているデータを使用させていただきました。

午後は、問題

 

非常に有意義な情報ばかりでしたので、かいつまんで以下ポイントを自分なりにまとめました。

ポイント

  • apacheのログは、ダブルクォーテーションをdelimiterに指定してあげるといい感じ
  • バッシュの開始と終了のプロセスが一番負荷がかかるので、なるべくまとめられうように工夫する
  • 並列処理すると、実際たたいておわるまでの時間がOS部分のAPIに対してのとプログラム自身が頑張った時間+OS部分に対して掛かって時間よりも早くおわる
  • 標準コマンドよりも、GNU系のコマンドのほうが処理速度がぜんぜん違う
  • 並列処理(マルチコア)してくれる、コマンドは最初に単機能コマンド実行するのがコツですので、でかいデータを扱う人は、注意しておく
  • `head *` でカレントディレクトリのファイルすべての先頭から10行表示してくれる
  • `cut -c` 文字数を指定(2文字目から12文字目)できる `cut -c2-12`
  • `cut -f`` フィールド列指定ができる (default delimiter = \t ごと) cut -f
  • 高速化のコツとして、一つ一つの処理をあえて分けるとコア数分だけ頑張ってくれる
  • エラー出力されるものは、historyに残らないので、明示的にexitでlogoutする
  • カーソルの移動とか、一個前の実行コマンドを↑とかで表示できるのは、Cursesがあるおかげ
  • Unixとして、標準入力(/dev/stdin)とか、標準出力(/dev/stdout)とか、エラー出力(/dev/stderr)をファイル単位で保存するのが考え方の基本
  • `awk`には、sleepがない AWK リファレンス | UNIX & Linux コマンド・シェルスクリプト リファレンス
  • `man ascii` で普段なれない文字コードのすべてがかかれてある。16進数の改行は0a
  • コマンドもbashから見ればテキストである
  • `tee` 流れ込んだファイルを一時的に保存を残しつつ、後ろの実行もできる make をしてる途中のファイルを残したいとき
  • echo コマンドは、改行コードをしらべたいときは、-n コマンドにする
  • 最新版のawkは、区切り文字 -Fに区切り文字が使われている gawkコマンド
  • sed Stream editor vi とコマンドは似て、そのふたつはedが祖。editorだけど、見えてないもの。 line editor
  • `tr -dc '1-9.'` で数字とドットだけ残したい
  • ping -c 回数分だけ飛ばす
  • grep -A1 特定の文字の次を飛ばす
  • sed -n 出したい範囲を正規表現で区切って出すことも出来る
  • grep -w A ワードとみなされる
  • yes ずっとy をつけさせる
  • tee 流れ込んだファイルを一時的に保存を残しつつ、後ろの実行もできる make をしてる途中のファイルを残したいとき
  • grep は行全体、awk は列を定義することはできる
  • grep で正確なものをしたいばあいは、正規表現を使う(grep コマンドそもそもが正規表現を使うッて意味)
  • 単機能は、処理は速い
  • 最終兵器は、最終段階のときにする
  • Linux の上に乗っているのは、もともとGNU系でC言語で書かれている
  • 最新のgrep コマンドは、数億行のデータを数秒でさばける
  • パフォーマンスチェック time コマンドを先頭つける
    • real 実際たたいておわるまで
    • user OS部分のAPIに対してのとプログラム自身が頑張った時間
    • sys OS部分に対して掛かって時間
  • xargs は、横表示 grep -o で縦表示
  • xargs だけだと、暗黙的に echo コマンドが実行される コマンドが指定されていればそのコマンドが実行される
  • 標準出力されたコマンドをパイプで繋いで、次に渡すときにどのコマンドすべてではないが、 - にわたすことができる
  • sed 's/置換元/置換先/g' 
    • s 置換
    • gをつけると、複数回実行してくれる
    • 置換先に& をつけると置換基の文字列がそのまま入る (←これはすごい勉強になった!)
    • 置換元に正規表現が使える、() でグループ化され、( と ) にはそれぞれ文字として意味があるので\ でエスケープする
    • 置換基に正規表現が使える、^ で先頭。$で文末。と言う意味となる
    • fold -w 2 で2文字ずつ文字列を分解する(sedの場合だったら、sed 's/../& /g')
  • gdate -d '2016-10-29 00:00:00' '+%s' s がUnixタイム -d で指定することができる省略時は実行した日
  • $() で実行した結果を標準出力してくれる `$(echo '60 * 60 * 24'|bc)` ->  86400

 

 

福岡サテライト会場では、イベント終了後も、ぱぴろん (@papiron) | Twitter さんの解説いただきました。ありがとうございました。

※私のマシンがmacGNU系を想定してます。

問1

ドメインからグローバルIPを引き出す問題でした。

nslookup コマンドを使ったやり方は、コマンド自体が非推奨とのことでしたので、dig コマンド ping コマンドを使ったやり方で。

$ dig www.usptomo.com | grep -A1 ';; ANSWER SECTION:' | grep -v ';; ANSWER SECTION:' | awk '{print $NF}'
157.7.203.188 

 grep -A で見つけた文字列の次after という意味で、数字を指定するとその数値分出してくれる。ただ、見つけた行を操作したいわけではないので、grep -v で無視した。awk のNFは列数なので、文末を表示したいときは、$NF でOK $0は1行全部 $1で1列目 ... とつづくようです。

 

$ ping -c 1 www.usptomo.com | head -n 1 | awk '{print $3}' | tr -dc '0-9.'

awk コマンド内に登場している$3ですが、3列目だけを出力している状態で、tr -dc で特定の文字だけつまり"数字とドットだけ残す"というやり方。

 

問2

 

2,3日前にネットを賑わせているあれからの問題。ネット芸人の力は計り知れない。メディアに影響を及ぼすこともあるんだなぁ。笑

togetter.com

ひらけ!ポンキッキ”という文字列を

ひらけ!ポンキッキ
らけ!ポンキッキひ
け!ポンキッキひら
!ポンキッキひらけ
ポンキッキひらけ!
ンキッキひらけ!ポ
キッキひらけ!ポン
ッキひらけ!ポンキ
キひらけ!ポンキッ

 

という風に表示したいとのこと。

結果からいうと

$ yes ひらけ!ポンキッキ | head -n 9 | awk '{for(i=1;i<NR;i++){printf "#"}printf $0$0;print ""}' | gtac |  cut -c 9-17

 というやり方で完了しました。

yesコマンドは、対話式コマンドといわれるもので、指定の文字列を入力するとそれをずっとだしてくれるものです。これを利用して、headコマンドで行数指定つまり、回数を9回指定する。awkコマンドでその行数分ループするわけですが上記からだとわからなかったので、内部のことを詳しく説明してくれました。

 

$ yes ひらけ!ポンキッキ | head -n 9 | awk '{for(i=1;i<NR;i++){printf "#"}printf $0$0;print ""}'
ひらけ!ポンキッキひらけ!ポンキッキ
#ひらけ!ポンキッキひらけ!ポンキッキ
##ひらけ!ポンキッキひらけ!ポンキッキ
###ひらけ!ポンキッキひらけ!ポンキッキ
####ひらけ!ポンキッキひらけ!ポンキッキ
#####ひらけ!ポンキッキひらけ!ポンキッキ
######ひらけ!ポンキッキひらけ!ポンキッキ
#######ひらけ!ポンキッキひらけ!ポンキッキ
########ひらけ!ポンキッキひらけ!ポンキッキ 

 一旦、# つきでひらけポンキッキを2回出して、

 

$ yes ひらけ!ポンキッキ | head -n 9 | awk '{for(i=1;i<NR;i++){printf "#"}printf $0$0;print ""}' | gtac
########ひらけ!ポンキッキひらけ!ポンキッキ
#######ひらけ!ポンキッキひらけ!ポンキッキ
######ひらけ!ポンキッキひらけ!ポンキッキ
#####ひらけ!ポンキッキひらけ!ポンキッキ
####ひらけ!ポンキッキひらけ!ポンキッキ
###ひらけ!ポンキッキひらけ!ポンキッキ
##ひらけ!ポンキッキひらけ!ポンキッキ
#ひらけ!ポンキッキひらけ!ポンキッキ
ひらけ!ポンキッキひらけ!ポンキッキ 

tac コマンドでひっくり返して、cut -c 9-17 で、ひらけ!ポンキッキ の次のひらけ!ポンキッキ を取り出すというやり方。GNU版cutコマンドはマルチバイト非対応だったそうなので、文字列の取り出し方は、awkをつかったやりかたで

###Linux用解答###
$ yes ひらけ!ポンキッキ | head -n 9 | awk '{for(i=1;i&lt;NR;i++){printf "#"}printf $0$0;print ""}' | tac | awk '{print substr($1,9,9)}'

 

問3

redirectのありがたみがよく言われる問題でした。通常ファイルに > で追記。 >> で上書きできるんですが、そのようなredirect機能をrbashすると奪われてしまう。保管も聞かなくて、不便な思いをしました。失って気がつくことってこうゆうことなんだなぁと感じました。

 

回答ですが、

eban (@eban) | Twitter さんの回答がすごくて何通りも文字列をファイル出力方法があるだなんて知らなかったです。ww

実は、rubyの開発やnkf コマンドの開発に携わってるすごい人らしいです。シェル芸界には、たくさんそうゆう人がいるんだろうなぁ。。

 

この問題を通して、Unixとして、標準出力とか、エラー出力をファイル単位で保存する考え方を知ることができました。

 

問4

 

以下のひらがなからワンライナーを始めて、濁点がつく字だけに濁点をつけてみてください。

$ echo すけふぇにんけん

という問題ですが、これめちゃくちゃ難しくてわけわかりませんでしたww

nkf コマンドには、様々な文字コードに変換できるということだけわかりました!笑笑

 

やり方としては、nkfをとおしてかたかなにして、半角カタカナにして、濁点の文字コードをつけて、ひらがなに戻して表示!みたいな。やばかったですww

 

問5

 プログレスバーのようなあの動きをコマンドラインで表現してくださいって問題でした。

 

yes | head | awk '{for(i=1;i<=NR;i++){printf "*"}print ""}' | sed "s/^/printf '/" | sed "s/$/\\\\r'; sleep 1/" | sh

これもわかりやすく解説していただいて、シェルに以下のように標準出力をコマンドに渡すということで、これも覚えておいて役に立ちそうなやり方でした。

$ 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

”改行せずに”先頭に戻り出力するというやり方。

問6

 

文字を解読すると言う問題でした。

慣れが必要みたいで、0a のところで、16進数の改行と気づくとその後のアプローチができるみたいです。

file コマンドに、どうやら様々なファイル形式を文頭を見て判別してるらしい。

The magic tests are used to check for files with data in particular fixed formats. The canonical example of this is a
binary executable (compiled program) a.out file, whose format is defined in <elf.h>, <a.out.h> and possibly <exec.h> in
the standard include directory. These files have a ``magic number'' stored in a particular place near the beginning of
the file that tells the UNIX operating system that the file is a binary executable, and which of several types thereof.
The concept of a ``magic'' has been applied by extension to data files. Any file with some invariant identifier at a
small fixed offset into the file can usually be described in this way. The information identifying these files is read
from the compiled magic file /usr/share/file/magic.mgc, or the files in the directory /usr/share/file/magic if the com-
piled file does not exist.

 

 

問7

本日(2016年10月29日)の範囲の毎秒のUNIX時刻で素数となるものを全て列挙してください。出力はUNIX時刻でなく、何時何分何秒か分かるようにしましょう。世界標準時で考えてください。という問題。

 

標準出力した結果をどうやって渡すか考えてました。

seq 0 86399 | xargs -I@ gdate -d '2016-10-29 00:00:00 @ sec' '+%s'

最終的に、こうなりました。 コマンドの実行回数を減らすことが高速化のコツとのことで。

seq 0 86399 | sed -e "s/^/2016-10-29 00:00:00 /" -e "s/$/ sec/" | gdate -f - '+%s' | gfactor | awk 'NF==2{print $2}'

 

問8

時間の都合上省略、幾何学的な問題でした

 

 

まとめ

場所をていきょうしていただいたベータソフトさんにも感謝ですし、解説をしていただいた福岡のぱぴろん (@papiron) | Twitter さんありがとうございました!オンラインで同じ時間を共有できてとても楽しい時間だったと思います。また次回も参加したいと思います。