第29回シェル芸勉強会へ遠隔参加
梅雨なのにあんまり雨が降らずこれはアカンと思う今日この頃です。通例では偶数月がシェル芸勉強会の月だが、今回は7月1日での開催となりました。今回も株式会社エスコ様(旧ベータソフト様)の会議室を利用させていただきました。会議室管理者の方ありがとうございます。 問題作成と解説の上田さん、ありがとうございます。参加者の皆様もお疲れ様でした。
会場への移動中は暑かったですね!今回福岡サテライトは5人の参加者が集いました。 この暑い中会場へ移動の移動お疲れ様でした。初参加の方もいらっしゃいました。
勉強会の情報
勉強会主催者上田さんが公開されているページをご覧ください。
午前の部
強烈eval系ワンライナーで有名な鳥海さんによるPerl入門でした。ちなみにPerl歴0年だとのことです。
ワンライナーが書きやすい機能に改めて気が付いた内容でした。
- ダイヤモンド演算子
- 豊富な特殊変数
-nae
などの起動オプション
そういえば豊富な特殊変数については、こんなことを思い出しました。Rubyにも同様な機能がありますね。
#シェル芸 Perlの特殊変数はなかなか覚えられなかったので use English を使っていた覚えが。
— ぱぴろんちゃん😱🙀 (@papiron) 2017年7月1日
use English
宣言をすると、特殊変数に英語名が利用可能です。
$ perl -e '$"=":";@a=(1,2,3);print "@a"' 1:2:3 $ perl -e 'use English;$LIST_SEPARATOR=":";@a=(1,2,3);print "@a"' 1:2:3
昼食
お昼は定番となった餃子の王将でした。開発環境の統一性に関する政治力の考察!?などの話題が上がりました。
午後の部
午後の部開始前にイントロの話をしました。富士山撮影に失敗したとです・・・ そしてlessコマンド終了時にショボーンとなる人たちの救済が必要なのを感じた次第です。
そしていよいよ午後の部開始です。今回は地獄の三部構成とのことです、楽しみですね。
福岡サテライトで考えて解説した内容は下記の通りです。
Q1
問題の意図としてはjoin
コマンドを使うんだろうと思いつつ、awk
の連想配列を使った解答例を考えました。
1列目と2列目をセットでキーにしています。
$ cat kadai1 kadai2 | awk '{a[$1" "$2]+=$3}END{for(v in a)print v,a[v]}' | sort -k1,1n 004 今泉 22 001 山田 40 005 鳥海 88 002 出川 30 003 上田 15
もう一つTukubaiのsm2
コマンドを使った例です。
自然に2列をキーにする場合は、awkよりも自然に記載出来て便利ですね。
$ cat kadai1 kadai2 | sort -k1,1n | sm2 1 2 3 3
Q2
ようやくjoin
コマンドを発動しました。-a
オプションを使った補完の出番です。
途中まで出来たけどここから?と思っていましたが、あとはawk
でゴニョゴニョやればよかったですね。
$ cat attend6 | tr ',' '\n' | sort | sed 's/$/ 出/' | join -a 1 attend - 001 山田 出出欠出出 出 002 出川 出出欠欠欠 003 上田 出出出出出 出 004 今泉 出出出出出 005 鳥海 欠出欠出欠 出
勉強会後、このブログを書いている時に考えた解答例がこちらです。
GNU coreutils付属のjoin
コマンドを使う想定で、-o
オプションと-e
オプションを追加しました。
$ cat attend6 | tr ',' '\n' | sort | sed 's/$/ 出/' | join -a 1 -o 1.1 1.2 1.3 2.2 -e 欠 attend - | sed 's/ \(.\)$/\1/' 001 山田 出出欠出出出 002 出川 出出欠欠欠欠 003 上田 出出出出出出 004 今泉 出出出出出欠 005 鳥海 欠出欠出欠出
macOSやFreeBSD付属のjoinコマンドを使う場合はこちらです。
-o
オプションでの列指定がコンマ区切りになるのに注意しましょう。
$ cat attend6 | tr ',' '\n' | sort | sed 's/$/ 出/' | join -a 1 -o 1.1,1.2,1.3,2.2 -e 欠 attend - | sed 's/ \(.\)$/\1/' 001 山田 出出欠出出出 002 出川 出出欠欠欠欠 003 上田 出出出出出出 004 今泉 出出出出出欠 005 鳥海 欠出欠出欠出
Q3
色々ゴニョゴニョやってみたが上手く出来ませんでした。
Q4.1
まずは各行の文字列長を1列目に付けてみます。
$ echo -1 4 5 2 42 421 44 311 -9 -11 | tr ' ' '\n' | sort -n | awk '{print length,$0}' 3 -11 2 -9 2 -1 1 2 1 4 1 5 2 42 2 44 3 311 3 421
更に数値がマイナスの行の文字列長にマイナスを付けます。
$ echo -1 4 5 2 42 421 44 311 -9 -11 | tr ' ' '\n' | sort -n | awk '{print length,$0}' | sed '/-/s/^/-/' -3 -11 -2 -9 -2 -1 1 2 1 4 1 5 2 42 2 44 3 311 3 421
後は1列目をキーにして横並びの処理をすれば完成です。
$ echo -1 4 5 2 42 421 44 311 -9 -11 | tr ' ' '\n' | sort -n | awk '{print length,$0}' | sed '/-/s/^/-/' | awk '{a[$1]=a[$1]" "$2}END{for(v in a)print v,a[v]}' | sort -k1,1n | awk '{$1="";print}' -11 -9 -1 2 4 5 42 44 311 421
Q4.2
この問題はやる時間が取れませんでした。
sort
コマンドの-g
オプションを使えば+符号がついても数値による並べ替えが出来ることを知りました。
$ echo 4 +5 -1 -2 | tr ' ' '\n' | sort -n -2 -1 +5 4 $ echo 4 +5 -1 -2 | tr ' ' '\n' | sort -g -2 -1 4 +5
Q5
データの回転をするには天地返し+行列変換をすればいいのでtac | rs -T
の技が使えないか?と考えました。
macOSやFreeBSDの場合はtac
コマンドの代わりにtail -r
を使います。(GNU coreutilsのtacを入れる手もあり)
$ seq 1 9 | xargs -n 3 1 2 3 4 5 6 7 8 9 $ seq 1 9 | xargs -n 3 | rs -T | tac 3 6 9 2 5 8 1 4 7
相手のデータは三角なので四角にすればいいのでは?と考えている間にタイムアップでした。
Q6
まずは問題のデータと全ての素数を混ぜ合わせて並べ換えます。ここまでは出題者上田さんの解答とほぼ同じでした。
$ seq 1 100 | gfactor | awk 'NF==2{print $2}' | cat <(cat prime | tr ' ' '\n') - | sort -n 2 2 3 3 (出力は省略) 89 89 97 97
ここから重複の数を数えます。
$ seq 1 100 | factor | awk 'NF==2{print $2}' | cat <(cat prime | tr ' ' '\n') - | sort | uniq -c | sort -s -k2,2n 2 2 2 3 (出力は省略) 1 23 1 29 (出力は省略) 2 89 2 97
後は1列目が1の時に改行して完成です。
$ seq 1 100 | factor | awk 'NF==2{print $2}' | cat <(cat prime | tr ' ' '\n') - | sort | uniq -c | sort -s -k2,2n | awk '$1==2{printf $2" "}$1==1{print ""}' | awk NF 2 3 5 7 11 13 17 19 31 37 41 43 47 53 59 67 71 73 79 83 89 97
Q7
まずはnkf
コマンドに実体参照の復号機能があることを解説しました。
後は頑張ってタグを取れば良さそうですがタイムアップでした。
$ echo 'a b' | nkf --numchar-input a b
後はテキストブラウザのw3m
を使う解答例もあります。
$ w3m -dump nyaan.html "m mmm "m m" "" m"" m m m" # mm#m"#"m "" "" m#m # m # "m mm" m" # m "# """" # m" "mm"
Q8
考えているうちに以前にやっていた「バナー芸人衆」という遊びを思い出しました。
http://papiro.hatenablog.jp/entry/2016/09/24/164244papiro.hatenablog.jp
この記事をヒントに考えていたら、「行列変換して空白だけの行を削除」すればいいやん!と閃いた解答例がこちらです。
まずは空白を-
に置換して行列変換します。
$ cat shellgei | tr ' ' '-' | sed 's/./& /g;s/ $//' | rs -T - - - - - - - - - - - - - - - - - - m - - " - - - " m - - m - - - " - " - m - - - m - - - " - - - - - - - " - - - - - - m - - - - - - - " - - - - - - m - - - - (出力は省略) - - - " - - - - - - - " - - - - - - m m # # - - - - " - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
次のgrep -v '^[- ]*$'
で空白と-
だけの行を(つまり元は空白だけの列)削除します。
後は2つめのrs -T
で元に戻し、-
を空白に戻して完成です。
$ cat shellgei | tr ' ' '-' | sed 's/./& /g;s/ $//' | rs -T | grep -v '^[- ]*$' | rs -T | tr -d ' ' | tr '-' ' ' m ""m m "m # # # # mm # # #mmm""" m" " m" mmm"" # # # m" # mm""m m" #mm m" # m" " # # "mm"" """" "m" #" m" #
午後の部終了後
スライドまで準備する時間がなかったので、メモを元に話をしました。
バナー芸人衆
Q8を解くのにヒントになったブログ記事についてちらっと解説しました。端末遊びで頭を鍛えましょう。
真・マイナンバーシェル芸
xargsの話題が出たので、-Pオプションで並列化が出来るという例をちらっと話しました。
X.509デジタル証明書の話題
先日自前で運用しているCAを使ってSSL/TLS用サーバ証明書の発行をしていたのですが、うっかり不正の日付で期限を指定した証明書を発行してしまったのでした。
期限が6月31日だって!?
— ぱぴろんちゃん😱🙀 (@papiron) 2017年6月27日
$ openssl x509 -noout -enddate -in cert.pem
notAfter=Jun 31 23:59:59 2018 GMT
Google Chromeが証明書で発行先のサーバ情報を参照する際に、CNではなくてSubjectAltNameをチェックするというように仕様が変わっていました。エラーが出てありゃ困ったぞ?という経験をしました。
では改めてX.509デジタル証明書のデータを見てみようということで話をしました。例としてTwitterが使っているSSLサーバ証明書を使いました。デジタル証明書のDER形式のバイナリデータを復元するワンライナーがこちらです。
バイナリデータの可視化はodコマンドを使っているオプションで-t x1c
と指定すると、1バイトずつ16進数とASCII文字で出力してくれるので便利です。
$ openssl s_client -connect twitter.com:443 < /dev/null 2> /dev/null | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | sed '1d;$d' | base64 --decode | od -t x1c | head 0000000 30 82 06 89 30 82 05 71 a0 03 02 01 02 02 10 6f 0 202 006 211 0 202 005 q 240 003 002 001 002 002 020 o 0000020 d0 1d 01 74 61 47 18 6f 80 92 a0 39 f3 b3 a8 30 320 035 001 t a G 030 o 200 222 240 9 363 263 250 0 0000040 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 7e \r 006 \t * 206 H 206 367 \r 001 001 \v 005 \0 0 ~ 0000060 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 1d 30 1 \v 0 \t 006 003 U 004 006 023 002 U S 1 035 0 0000100 1b 06 03 55 04 0a 13 14 53 79 6d 61 6e 74 65 63 033 006 003 U 004 \n 023 024 S y m a n t e c
X.509デジタル証明書はASN.1記法に基づいたデータ構造になっており、OpenSSLのasn1parseサブコマンドで可視化出来ます。
$ openssl s_client -connect twitter.com:443 < /dev/null 2> /dev/null | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | sed '1d;$d' | base64 --decode | openssl asn1parse -i -inform DER 0:d=0 hl=4 l=1673 cons: SEQUENCE 4:d=1 hl=4 l=1393 cons: SEQUENCE (出力は省略) 174:d=2 hl=2 l= 30 cons: SEQUENCE 176:d=3 hl=2 l= 13 prim: UTCTIME :150902000000Z 191:d=3 hl=2 l= 13 prim: UTCTIME :170730235959Z 206:d=2 hl=3 l= 137 cons: SEQUENCE (出力は省略) 326:d=4 hl=2 l= 18 cons: SEQUENCE 328:d=5 hl=2 l= 3 prim: OBJECT :commonName 333:d=5 hl=2 l= 11 prim: UTF8STRING :twitter.com (出力は省略) 650:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Alternative Name 655:d=5 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:301E820B747769747465722E636F6D820F7777772E747769747465722E636F6D
UTCTIME
の2つ目が期限日です。YYMMDDHHMMSS形式にZ
がついた文字列になっています。最後に付いたZがUTC時間の意味です。ここで重要なのは、期限日は文字列であるということです。つまり不正な日付も入れることが出来るのです。
UTF8STRING
の部分がCommonName(CN)と呼ばれる部分で、サーバ証明書の場合は通常ホスト名を指定します。しかし最近のGoogle Chromeは、証明書のホスト名を次のOBJECT :X509v3 Subject Alternative Name
で参照するようになりました。実際のデータは16進数のバイナリデータとなっています。ホスト名が2つ登録されているが見えるでしょうか?
$ echo 301E820B747769747465722E636F6D820F7777772E747769747465722E636F6D | xxd -p -r | od -t x1c 0000000 30 1e 82 0b 74 77 69 74 74 65 72 2e 63 6f 6d 82 0 036 202 \v t w i t t e r . c o m 202 0000020 0f 77 77 77 2e 74 77 69 74 74 65 72 2e 63 6f 6d 017 w w w . t w i t t e r . c o m 0000040
openssl x509
でSubject Alternative Nameを可視化したのがこちら。2つのホスト名が登録されているのがわかります。
$ openssl s_client -connect twitter.com:443 < /dev/null 2> /dev/null | sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' | sed '1d;$d' | base64 --decode | openssl x509 -inform DER -noout -text | grep -A1 'Subject Alternative Name' X509v3 Subject Alternative Name: DNS:twitter.com, DNS:www.twitter.com
バイナリデータを閲覧するコマンドについて補足をしておきました。macOSやFreeBSDのod
コマンドはマルチバイト文字に対応しているので便利ですね。
$ echo 'The シェル芸 Power' | od -t x1c 0000000 54 68 65 20 e3 82 b7 e3 82 a7 e3 83 ab e8 8a b8 T h e シ ** ** ェ ** ** ル ** ** 芸 ** ** 0000020 20 50 6f 77 65 72 0a P o w e r \n 0000027 $ echo -n '🍣食いねえ' | od -t x1c 0000000 f0 9f 8d a3 e9 a3 9f e3 81 84 e3 81 ad e3 81 88 🍣 ** ** ** 食 ** ** い ** ** ね ** ** え ** ** 0000020
またvimに付属のxxd
コマンドについても説明しました。-p
や-p -r
オプションを使った16進数への符号化と復号化、-b
オプションでビット列(2進数)への符号化など。
$ echo 'The シェル芸 Power' | xxd -p 54686520e382b7e382a7e383abe88ab820506f7765720a $ echo 'The シェル芸 Power' | xxd -p | xxd -p -r The シェル芸 Power $ echo 'The シェル芸 Power' | xxd -b 00000000: 01010100 01101000 01100101 00100000 11100011 10000010 The .. 00000006: 10110111 11100011 10000010 10100111 11100011 10000011 ...... 0000000c: 10101011 11101000 10001010 10111000 00100000 01010000 .... P 00000012: 01101111 01110111 01100101 01110010 00001010 ower.
その後雑談を兼ねたいろんなお話が出てきました。皆さんいろんな環境でいろんな苦労をされていらっしゃいますね。
シェル芸勉強会って「データに着目する」視点が身に付くんだなあと思いました。シェル芸はデータストリームな処理なので、必然的にデータに着目するからかもしれないですね。
今回は初参加の方もいらっしゃったので、もう少し自己紹介などの時間を取ればよかったと思いました。自分なりに解説やサポート、話題提供を頑張ったつもりですが、もう少し肩の力を抜いていいかもしれないです。