「第31回シェル芸勉強会:福岡サテライト」レポート
前回に引き続き鬼の忙しさが続いていましたが、忙しさに若干の切れ目が出来たのでシェル芸勉強会の福岡サテライトを開催いたしました。会場は今回もAIP Cafeです。問題出題と解説の上田さん、ありがとうございます。参加者の皆様もお疲れ様でした。
午前中は会社の会議の後、昼からはシェル芸地獄というなかなかハードな一日でした。10月になったけど昼はまだまだ暑いです。福岡サテライトでは総勢4人の参加者が集いました。久しぶりの参加の方もいらっしゃいました。
勉強会の情報
勉強会主催者上田さんが公開されているリンク集をご覧ください。
午後の部
今回は午後の部のみ。今回もイントロのスライドとか準備出来ませんでした。 せっかくなので持ってきたSoftware Design 9月号と10月号の宣伝をさせていただきました。
こんなリスケジュールは嫌だ!という時に便利な?シェル関数作って投稿しましたよ(・ω・)ノ #gihyo #sd #シェル芸 pic.twitter.com/xfMRo4HQjv
— ぱぴろんちゃん😱🙀 (@papiron) 2017年9月29日
問題が難しい場合は関連情報の解説や補足を重視して、今回も福岡サテライトなりのペースで進めていく所存です。
Q1
行をまたいだ置換の問題。工夫すればsed
で出来そうだが難しい・・・
解答例を後から見ることにして、福岡では補足や解説が重要なのでsed
の後方参照やy
コマンド、拡張正規表現などの説明をしました。
後方参照出来る数は9個までだよとか。
# こんな感じで\( \)で囲った部分を\1\2で順番に参照出来るよ $ echo 123-45 | sed 's/^\(...\)-\(..\)/\1\2/' 12345 # GNU sedの-rオプションで拡張正規表現が使える。参照したい部分の囲みにバックスラッシュが不要。 $ echo 123-45 | gsed -r 's/^(...)-(..)/\1\2/' 12345 # yコマンドで変換だぞ $ echo 12345 | sed 'y/12345/abcde/' abcde $ echo 12345 | sed 's/12345/a/' a # trコマンドでも出来る。sedより高速だが動きが違う部分も。 $ echo 12345 | tr '12345' 'abcde' abcde $ echo 12345 | tr 12345 a aaaaa
出題者上田さんの解答例では、Perl正規表現のルックアラウンドアサーションを使っていました。復習せにゃ、、 Perlの正規表現はPCREとしてライブラリで提供されており、PHPやApache HTTP Serverなどでも利用可能だよとか補足解説しました。
PCRE - Perl Compatible Regular Expressions
Q2
上の行に合わせたインデントをする。Vimのインデントコマンド=
の出番?と思いましたが、出来ませんでした。
Vimでやっている人はいらっしゃいました。
vim だと qqywq/^\*@q/^\*@q/^\*@q/^\*@q でできるけど汚い #シェル芸
— のぎろ (@nogiro_iota) 2017年10月7日
ちなみにJavaScriopt(node)で解いた方もいらっしゃって、私には理解が難しく。
JSならば、こうでしょうか??https://t.co/LVytgMNkzL#シェル芸
— ぷる@イボ治療中 (@butackle66) 2017年10月7日
Q3
問題の図形を見て素数がすぐに思いつく方々、、、 シェル芸界隈では定番になってきていますが、福岡サテライトではまだあまり知られていないので、素数列挙の定番ワンライナーを解説しました。
$ seq 1 20 | gfactor | awk 'NF==2{print $2}' 2 3 5 7 11 13 17 19
後はawk
で頑張ればなんとかなりそうです。
Q4
福岡サテライトの参加者は、ASCIIコードやバイナリデータの扱いに慣れていない方が多かったので補足解説しました。
# まずはマニュアルを見て見る $ man ascii # odコマンドでASCIIコードを16進数表記 $ echo -n ABCabc | od -t x1c 0000000 41 42 43 61 62 63 A B C a b c 0000006 # BSD版だとマルチバイトに対応してるよ。 $ echo -n おはようございます | od -t x1c 0000000 e3 81 8a e3 81 af e3 82 88 e3 81 86 e3 81 94 e3 お ** ** は ** ** よ ** ** う ** ** ご ** ** ざ 0000020 81 96 e3 81 84 e3 81 be e3 81 99 ** ** い ** ** ま ** ** す ** ** 0000033 # vim付属のxxdコマンドで16進数表記。 $ echo -n おはようございます | xxd 00000000: e381 8ae3 81af e382 88e3 8186 e381 94e3 ................ 00000010: 8196 e381 84e3 81be e381 99 ........... # -uオプションで10進数表記 $ echo -n おはようございます | od -t u1c 0000000 227 129 138 227 129 175 227 130 136 227 129 134 227 129 148 227 お ** ** は ** ** よ ** ** う ** ** ご ** ** ざ 0000020 129 150 227 129 132 227 129 190 227 129 153 ** ** い ** ** ま ** ** す ** ** 0000033 # -bオプションで2進数表記。ビット列が見えるよ。 $ echo おはようございます | xxd -b 00000000: 11100011 10000001 10001010 11100011 10000001 10101111 ...... 00000006: 11100011 10000010 10001000 11100011 10000001 10000110 ...... 0000000c: 11100011 10000001 10010100 11100011 10000001 10010110 ...... 00000012: 11100011 10000001 10000100 11100011 10000001 10111110 ...... 00000018: 11100011 10000001 10011001 00001010 ....
Q5
Q4でのASCIIコードやバイナリデータに解説してたら、時間が無くなっちゃいました。 解答例に出てきたプロセス置換について解説をしました。
# 2つのコマンドの出力結果をdiffで比較 1d0 < 1 3a3 > 4
シェルの本質的な機能は、コマンドの実行とプロセス管理、ファイルをベースとしたデータ入出力の管理、だと思いますといった話をしました。
補足として/dev/urandom
、/proc
ファイル、bash
のtcp通信機能など、ファイルをベースとした機能も解説しました。
# 乱数取得のワンライナー。trの-dcオプションについても補足解説。 $ cat /dev/urandom | LANG=C tr -dc '0-9' | fold -w 10 | head 5185330642 4941724968 8040182629 4953975155 7776003351 2268161449 3516241602 2661394339 7045619931 7999489264
Q6
UTF-8な日本語ひらがなを小文字に変換する問題。福岡サテライトの参加者から文字コードをズラせばいいんじゃない?という意見が出ました。 じゃあ確認してみましょう。
$ echo -n あぁ | xxd -p | fold -w 6 e38182 e38181
ということで、3バイト毎にまとめてコード番号を1引けば良さそう。ということで考えた解答例がこちらです。ご意見ありがとうございました。
$ echo -n あいうえお | xxd -p -u | fold -w 6 | sed 's/$/ - 1/' | (echo 'obase=10;ibase=16'; cat) | bc | (echo 'obase=16'; cat) | bc | xxd -p -r ぁぃぅぇぉ
bc
コマンドについては知られていなかったので補足解説しておきました。
bash
やbc
自体の履歴機能(readline)が使えるので、繰り返し計算する時の電卓替わりにも便利だったりします。
Q7
アニメーションを作る問題。ギブアップでした。
そもそも端末でアニメーションを作るってどういう意味?という疑問が出たので、下記のような事例の解説をしました。
例えば1秒毎に@
が一つずつ伸びていくにはこんな感じとか。
# まずはコマンド列を作成する $ seq 3 | awk '{for(i=1;i<$1;i++){printf "@"}print ""}' | sed 's/^/clear; echo /' | sed 's/$/; sleep 1;/' clear; echo ; sleep 1; clear; echo @; sleep 1; clear; echo @@; sleep 1; # 上記のコマンドをシェルに流し込んで実行。 $ seq 3 | awk '{for(i=1;i<$1;i++){printf "@"}print ""}' | sed 's/^/clear; echo /' | sed 's/$/; sleep 1;/' | sh
Q8
全角5文字以内で折り返す問題。ギブアップでした・・・
Q9
スクレイピングの問題。CSVにするのは諦め元素記号だけを抽出するものを作りました、敗北感。
$ curl -s http://www.gadgety.net/shin/trivia/ptable/ | sed -n '/<tr /,/<\/tr>/p' | grep font | grep '<a href="' | grep -o '<a href="[^"][^"]*\.html">[^<>][^<>]*</a>' | sed 's/<[^<>]*>//g' H He Li Be (省略) Fm Md No Lr
午後の部終了後
スライドの準備などはしていませんでしたが、参加者の方でxargs
コマンドの使い方を色々悩んでた方がいらっしゃいました。
改めてxargs
コマンドについて基本から解説させていただきました。
xargsについて
配列的なデータを扱う
- 配列的な文字列データに対して、コマンド処理をまとめて実行する時に使う
- 区切り文字はスペース、タブ、改行、EOF
使い方の基本例です。
$ find dir -type f | xargs file $ echo {1..5} | xargs printf '%05d\n' 00001 00002 00003 00004 00005
コマンドを指定しない場合は、暗黙的にechoを実行します。
$ seq 10 | xargs -n 2 1 2 3 4 5 6 7 8 9 10
どんな処理をするコマンドか?
xargs
はHaskellやRuby、JavaScriptのmapに似ており、配列的なデータに対して繰り返し同じ処理を施します。
配列内の数字を2倍にする処理がこちらです。
# xargsコマンド $ seq 1 5 | xargs -I@ expr @ \* 2 2 4 6 8 10 # Ruby $ pry [1] pry(main)> [1,2,3,4,5].map do |i| i*2 end => [2, 4, 6, 8, 10] # JavaScript $ node > [1,2,3,4,5].map((n)=>{return n*2}) [ 2, 4, 6, 8, 10 ] # PHP $ psysh >>> array_map(function($num){return $num*2;},[1,2,3,4,5]) => [ 2, 4, 6, 8, 10, ]
オプション
-nオプション
一度に処理する数を指定します。-Iオプション
通常はコマンド列を最後に渡しますが、埋め込む文字列の指定が可能です。
$ seq 5 | xargs -I@ gdate -d '@ day' +%F 2017-10-08 2017-10-09 2017-10-10 2017-10-11 2017-10-12
- -0オプション
区切りをヌル文字\0
にします。ディレクトリ名やファイル名に空白などがある場合に便利です。
$ find 'VirtualBox VMs' -print0 | xargs -0 file
複数のコマンドを実行するには?
複数のコマンドやパイプなどは実行出来ませんが、sh
やbash
の-c
オプションを使うことで実現可能です。
1秒毎に1カウントアップするワンライナーがこちらです。
$ seq 10 | xargs -I@ sh -c 'echo @; sleep 1;' 1 2 (省略) 9 10
- -Pオプション
並列処理が可能です。「真・マイナンバーシェル芸」の記事を紹介しました。
ImageMagickとの実用的な組み合わせで、画像ファイルを再帰的に選択してフォーマットの変換やサムネイル作成の処理をするワンライナーを紹介しました。
# PNG=>JPGへフォーマット変換 $ find * -type f -name '*.png' | sed 's/\.png//' | xargs -I@ convert @.png @.jpg # 長辺が100x100ピクセル以内のサムネイル画像を作成。ファイル名に-sを追加。 $ find * -type f -name '*.png' | sed 's/\.png//' | xargs -I@ convert -geometry 100x100 @.png @-s.png
他にはcurl
コマンドでデカイデータをPOSTする際にコマンドライン長制限の問題が出て困った、という話題が上がりました。
これについてはデータをファイルから読んで出来るのでは?と予測。
curl
のマニュアルを見るとファイル名に@
を付けて指定すれば良さそうです。
今回も参加者の方々お疲れ様でした。