信じてやってみる「第24回シェル芸勉強会 大阪サテライト」の復習
はじめに
8月27日(土)に開催された「第24回シェル芸勉強会 大阪サテライト」に参加しました。
今回も朝10時から夜19時までsh行三昧の一日でした。
メイン会場および他サテライト会場
各会場の様子
- 第6回もう初心者向けでないなんて言わないよ絶対午前のシェル勉強会/第24回◯◯o◯裏番組シェル芸勉強会 - Togetterまとめ
- 「第24回シェル芸勉強会 大阪サテライト」レポート - くんすとの備忘録
- 第24回シェル芸勉強会へ遠隔参加 - 日々之迷歩
問題ならびに模範解答
ライブストリーム(YouTube)
午前の部: シェルに関する勉強会
- man gawkを1個ずつやってみる。
- 講師: Ryuichi Ueda(@ryuichiueda)さん
- gawkとは
- パターンとアクション
- -Fオプション
- -vオプション
- -fオプション
- –traditionalまたは–compatオプション
- -W lintオプション
- –re-intervalオプション
- BEGINブロック
- 型について
- 変数の初期化について
- 配列について
- 講師: Ryuichi Ueda(@ryuichiueda)さん
- 毎日叩けるシェル芸を覚えよう!
- 講師: ぐれさん(@grethlen)さん
- シェル芸の学び方
- ボトムアップなアプローチ
- トップダウンなアプローチ
- プチシェル芸勉強会
- パターンマッチ(grep)
解答例:
awk '$1~/^2016/' holidays
- 行数をカウント
解答例:
cat holidays | grep '^2016-02' | wc -l
- 特定のフィールドを抽出
解答例:
awk '/^2016-09-19/{print $3}' holidays
- コマンド置換
解答例:
grep $(date '+%Y-%m-%d') holidays
- 数え上げ(uniq -c)
解答例:
awk '/^2016/{print $2}' holidays | sort | uniq -c
- あるパターンからあるパターンまで抽出
解答例:
sed '/^2016-08-01/,/^2016-09-30/!d' holidays
- プロセス置換
解答例:
diff <(awk '/^2015/{print $3}' holidays | sort | uniq) <(awk '/^2016/{print $3}' holidays | sort | uniq)
- 上級者向け問題
解答例:
sed -rz 's/(20[^ ]+)( . [^ ]+ 1)\n(20[^ ]+)( . [^ ]+ 0)\n(20[^ ]+)( . [^ ]+ 1)/\1 \3 \5!\n/g' holidays | sed '/!$/!d' | sed 's/!$//'
- パターンマッチ(grep)
- 参考文献・支援ツール
- 講師: ぐれさん(@grethlen)さん
午後の部: シェル芸勉強会
- いずれの問題も、Debian 8「jessie」の32ビットPC版で解答しています。
- 使用OSによっては、sortを実行する言語環境をLANG=Cにする必要があるかも知れません。
Q1: 行毎の集計
$ awk '{for(i=1;i<=NF;i++)$i=="玉子"?a+=1:b+=1;print "玉子:"a" 卵:"b;a=b=0}' Q1
玉子:5 卵:1
玉子:3 卵:3
玉子:4 卵:2
玉子:1 卵:5
玉子:2 卵:1
各行での処理の最後に、変数aおよびbの値を「0」でリセットしておく必要があります。
Q2: 2つ目以降の重複した文字を削除
$ grep -o . Q2 | awk '!a[$1]++' | sed -z 's/\n//g;s/$/\n/'
へのもじ
まず、grep -o .で1行1文字にて出力します。
そして、awk ‘!a[$1]++’で最初に出現したものだけを抜き出します。
Q3: 第1フィールドをキーとしたレコード区切り
$ sort -k1,1 Q3 | awk '{print(p==$1?$0:"%%\n"$0);p=$1}END{print "%%"}'
%%
キム タオル
キム ワイプ
%%
金 正男
金 正日
金 日成
%%
まず、sort -k1,1で第1フィールドをキーとしてソートします。
そして、第1フィールドの値を保存したawkの変数pを使い、第1フィールドの値が変わったかを確認します。
Q4: Excelファイルからのデータ抽出
$ libreoffice --invisible --convert-to html Q4.xlsx >/dev/null; w3m -dump Q4.html | awk 'NR==1{print $1}'
114514
$ libreoffice --invisible --convert-to html Q4.xlsx >/dev/null; w3m -dump Q4.html | awk 'NR==4{print $1}'
エクシェル芸
LibreOfficeはCLIでも使える、ということでExcelファイルをHTMLファイルに変換してデータを抽出します。
unzip -pを使っていないので「エクシェル芸」とすれば邪道だとは思いますが、手軽かつ古い形式のExcelファイルも扱うことが可能ということで紹介しました。
ちなみに、unzip -pを使った別解は次のようになります。
$ unzip -p Q4.xlsx xl/worksheets/sheet1.xml | sed -rz 's/^.*(<c r="A1"[^>]*><v>[^<]+<\/v><\/c>).*/\1/' | sed -r 's/^.*>([^<]+)<.*/\1\n/'
114514
$ unzip -p Q4.xlsx xl/sharedStrings.xml | sed -rz 's/<\/si><si>/\n/g' | grep '^<t>' | sed "$(unzip -p Q4.xlsx xl/worksheets/sheet1.xml | sed -rz 's/^.*(<c r="A4"[^>]*><v>[^<]+<\/v><\/c>).*/\1/' | sed -r 's/^.*>([^<]+)<.*/\1\n/')"'!d' | sed -r 's/^<t>([^<]+)<.*/\1/'
エクシェル芸
unzip -pのあとは、sedを使って不要な部分を削っていけばOKです。
Q5: 数式のテンプレートファイルを使った計算
$ echo 2 | xargs -I@ bash -c 'sed "s/x/("@")/g" Q5 | bc -l'
6
2.50000000000000000000
8
echoの出力をxargs -Iに渡し(後の「@」は置換文字列)、生成したコマンド文字列をbashで処理します。
なお、コマンド文字列の最後にあるbcは小数を扱えるように-lオプションを指定しています。
Q6: 単語数の多い方で置換
訂正: 当日の解答は「玉子」の数を1つ多く出力してしまう誤った解答であったため、次のようなワンライナーに訂正します。
$ mecab Q6 | sed '$d' | awk '{a[$1]+=1}END{if(a["玉子"]>=a["卵"]){for(i=1;i<=NR;i++)printf "玉子"}else{for(i=1;i<=NR;i++)printf "卵"}printf "\n"}'
玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子玉子
最初に、形態素解析器mecabを使って「玉子」と「卵」を分別します。 次に、sedで次の処理で不要になる最終行を削除します。 最後に、awkの連想配列aで「玉子」と「卵」の数を数え、ENDブロックで「玉子」と「卵」のうち数が多いほう(ここでは「玉子」)をmecabおよびsedで生成したレコードの数に合わせて出力します。
Q7: 数字が昇順になっている行のみ出力
当日に解答できなかった問題です。
seq -wで得られた各行の数字について
- 「54321」から「12345」のように、各桁の数字を左から右へと昇順で並べ直す
- 各行の並べ直した数字を昇順でソート
- 重複した行を削除
すれば良いと考え、
$ seq -w 00000 99999 | xargs -I@ bash -c 'echo @ | grep -o . | sort | sed -z "s/\n//g;s/$/\n/"' | sort | uniq
としたものの処理が非常に重く、5桁から3桁に減らしても処理時間は
$ time seq -w 000 999 | xargs -I@ bash -c 'echo @ | grep -o . | sort | sed -z "s/\n//g;s/$/\n/"' | sort | uniq
000
001
002
(...中略...)
889
899
999
real 0m8.604s
user 0m0.048s
sys 0m0.108s
のように約9秒を要するため、
- 「11233」のように、全ての桁の数字について「左の桁の数字 <= 右の桁の数字」となっている行のみ出力
と考え方を切り替え、試しに3桁で処理時間を測ると
$ time seq -w 000 999 | sed -r 's/(.)/\1 /g' | awk '$1<=$2&&$2<=$3' | sed 's/ //g'
000
001
002
(...中略...)
889
899
999
real 0m0.017s
user 0m0.004s
sys 0m0.004s
のように圧倒的に良い結果が得られました。
結論として、最終的な解答としては
$ seq -w 00000 99999 | sed -r 's/(.)/\1 /g' | awk '$1<=$2&&$2<=$3&&$3<=$4&&$4<=$5' | sed 's/ //g'
00000
00001
00002
(...中略...)
88999
89999
99999
のようになります。
Q8: 日本数学オリンピック予選問題
Q8-1・Q8-2のいずれも、当日に解答できなかった問題です。
Q8-1: 1〜7を全て含む7桁の整数を列挙
まず、seqで「1234567」から「7654321」まで出力します。
そして、grep 1からgrep 7までを順にパイプでつないだものでふるいに掛けます。
$ awk 'BEGIN{printf "seq 1234567 7654321 ";for(n=1;n<=7;n++)printf "| grep "n" "}' | sh > tmp
grep 1からgrep 7まで書いていくと手間がかかるため、ここではawkでコマンド文字列を生成してshに渡しています。
Q8-2: 条件に合致する素数と各桁の数字を求める
$ paste <(sed -r 's/(.)/\1 /g' tmp) <(sed -r 's/(.)/\1 /g' tmp | awk '{print $1"*"$2"*"$3"*"$4"+"$5"*"$6"*"$7}' | bc | factor) | awk 'NF==9' | sed 's/:.*$//'
2 3 4 6 1 5 7 179
2 3 4 6 1 7 5 179
2 3 4 6 5 1 7 179
(...中略...)
6 4 3 2 5 7 1 179
6 4 3 2 7 1 5 179
6 4 3 2 7 5 1 179
生成したファイルtmpの各行にある、1〜7の全ての数字を含む7桁の整数「abcdefg」について「a * b * c * d + e * f * g = 素数」であるような行を抽出します。
LT大会・懇親会
- シェル芸でもIoTがしたい!
- 発表者: nmrmsys(@nmrmsys)さん
- Sphero SPRK+をCLIで操作
- Raspberry PiにNode.js環境を構築
- オレオレCLIは基本
- デモの実行(sphero discoコマンドなど)
- 今後の展望など
- 発表者: nmrmsys(@nmrmsys)さん
- お気軽!お手軽!スクレイピング!
- 発表者: T.Motooka(@t_motooka)さん
- スクレイピングとは
- 実例として、金融機関コード・支店コード・各名称を取得
- 対象サイトは大人の事情で秘密
- PhantomJSでDOM操作して標準出力
- 発表者: T.Motooka(@t_motooka)さん
- UTF-8テキストからUnicodeのコードポイントを求める@シェル芸
- 発表者: 小原一哉(@KoharaKazuya)さん
- UTF-8のコードポイントとは
- UTF-8は可変長バイト
- foldで折り返し
- dcで基数の変換
- 発表者: 小原一哉(@KoharaKazuya)さん
- QRコードシェル芸?
- 発表者: MSR(@msr386)さん
- QRコードとは
- qrencodeで生成
- zbarimgおよびzbarcamで読み取り
- シェル芸への応用
- 実例として、危険シェル芸をQRコードに格納
- 発表者: MSR(@msr386)さん
- Bash on Windows環境非破壊ハンズオン
- 発表者: くんすと(@kunst1080)さん
- Bash on Windowsをどこまで破壊しても大丈夫なのか確認
- /usrを破壊→大丈夫
- /binを破壊→大丈夫
- /libを破壊→大丈夫
- /etcを破壊→大丈夫
- これ以上の破壊活動は危険!!
- 発表者: くんすと(@kunst1080)さん
おわりに
午前の部の前半でブリーフィング、後半で訓練、午後の部で実戦といった感じでした。
午前の部では、プチシェル芸勉強会の問題の難度調整が良い塩梅になっていて、シェル芸未体験の人でもライブストリームを一通り見て実際に手を動かせばシェル芸の手軽さと便利さを感じることができるのではないかと思います。
午後の部は相変わらず問題の難度が高く、特にQ7とQ8では中学・高校のテストで50点以上だったことがない自分の数学力の低さを改めて知ることになりました。
あと、モノの動き方を知るにはとりあえず壊してみるのが一番の近道なのではないか、と少々危険なことを思い浮かべました。
最後に、
- 大阪サテライトの主催をされている、くんすと(@kunst1080)さん
- 大阪サテライト会場を提供してくださっている、フェンリル株式会社 大阪本社様
- Ryuichi Ueda(@ryuichiueda)さんをはじめとして、メイン会場スタッフおよび講師の皆さん
に改めてお礼を申し上げます。ありがとうございました。