「第38回シェル芸勉強会:福岡サテライト」レポート
文化の日ではありますが、シェル芸で頭脳をぶっ叩くのも乙な感じではないでしょうか?世間一般は文化的祝日ですが、福岡サテライト会場に3人が集まりました。遠くは長崎から参加ありがとうございます。福岡サテライト開催も19回目になりました。問題作成解説の上田さん、ありがとうございます。参加者の皆様もお疲れ様でした。
勉強会の情報
勉強会主催者上田さんが公開されているリンク集をご覧ください。
最近シェル芸界隈で合成文字が流行しておりますが、その影響でイベント通知ページのタイトルが見難い上にググられにくくなっております。
午前の部
午前中は鳥海さんによる文字コードの解説を聞きました。
twitter.comご静聴ありがとうございました。本日の午前の部で使用した資料です。
— Hidekazu Toriumi (@hid_tori) 2018年11月3日
次回、最終回予定です。https://t.co/JFCN8m1wso#シェル芸
EUCやShift-JISの成り立ちが面白かったのと、Shift-JISのトリッキーなコードに闇を感じました。 ワンライナーで文字コード表やネストした三項演算子が繰り出される時間でした。 ダメ文字問題なんてあったのを知りました。
次回はようやくUnicodeの世界へ突入らしいですが、文字コードの設計ってすごく大変そうです。
vimに付属しているバイナリダンプやでコードが出来るxxdコマンドですが、 -uオプションで大文字出力が出来るのがあまり知られてないようでした。 xxdのオプションについては、マニュアルで一度確認してみましょう。 読み出しのオフセットや長さ指定、出力の列数、エンディアンの切り替えなどの指定が出来ます。
昼食
参加者みんなで会場近くのパレスチナ料理屋「D-TRANOI」という店へ入ってみました。
香辛料がすごく効いたサッパリした辛さでした。しかし食べるうちに口がヒリヒリしてきて、かなーり辛いが美味いですね。
戻ってからしばらくしてイントロを話しました。バッチファイルやってみた的な。
午後の部
今回は私も問題にじっくり対峙していた感がありましたが、 福岡サテライトでもサポートや解説はいつもより控えめで大丈夫だった気がします。 参加者の方々がレベルアップしてきたのを感じた日でした。
Q1
モヤモやを除去・・・要するに結合文字を分離しろということでしょうか? 解答例は出題者上田さんの解説を参照いただいて、ここではモヤモヤ文字がどうなっているかを確認してみます。
MacやFreeBSD付属のod
コマンドは、マルチバイトに対応しておりで確認に使いやすいと思います。
16進数のコードでd288やd289になっている部分がモヤモヤの部分です。
$ echo 'jus共催 第38回҈҈҉҈҈҉シ҈҉ェ҈҉ル҈҉芸҈҉勉҈҉強҈҉会' | od -t x1c 0000000 6a 75 73 e5 85 b1 e5 82 ac 20 e7 ac ac 33 38 e5 j u s 共 ** ** 催 ** ** 第 ** ** 3 8 回 0000020 9b 9e d2 88 d2 88 d2 89 d2 88 d2 88 d2 89 e3 82 ** ** ҈ ** ҈ ** ҉ ** ҈ ** ҈ ** ҉ ** シ ** 0000040 b7 d2 88 d2 89 e3 82 a7 d2 88 d2 89 e3 83 ab d2 ** ҈ ** ҉ ** ェ ** ** ҈ ** ҉ ** ル ** ** ҈ 0000060 88 d2 89 e8 8a b8 d2 88 d2 89 e5 8b 89 d2 88 d2 ** ҉ ** 芸 ** ** ҈ ** ҉ ** 勉 ** ** ҈ ** ҉ 0000100 89 e5 bc b7 d2 88 d2 89 e4 bc 9a 0a ** 強 ** ** ҈ ** ҉ ** 会 ** ** \n 0000114
Q2
ファイルの中に記載された文字だけを抽出する問題。
私の解答はuniq
の-d
オプションで重複チェックを利用したものです。
$ echo 不摂生 | grep -o . | cat - <(cat 仏説摩訶般若波羅蜜多心経 | grep -o . | sort | uniq) | sort | uniq -d 生 不
出題者上田さんの解答はgrep
の-f
オプションを使っていました。
福岡サテライトでは、grep
の-f
オプションについて補足説明をしました。
Q3
スクレイピングで元号リストを取得する必要がある問題。w3m
を使って楽をしちゃいました。
福岡サテライト参加者の中から「grep
の-f
オプションを使うといいんじゃ?」という意見が出まして、
ということで下記のようなスッキリとした解答が出せました。
# 何度もアクセスすると迷惑なのでファイルに保存 $ w3m -dump https://edo100.tokyo/edogengolst/ | sed -n /天文/,/明治/p | awk '{print $1}' > gengo $ cat gengo | grep -f edo 元和 元禄 享保 安政
Q4
bashのブレース展開の問題でした。
{A..Z} {A..Z}{A..Z} {A..Z}{A..Z}{A..Z}
と3つのブレース展開を使えば良かったですね。
Q5
ガウス分布の乱数発生は「シェルプログラミング実用テクニック」、 ヒストグラムを描く問題は「シェルスクリプト高速開発入門」にあったのを思い出しました。 どちらも問題出題者上田さんの著書です。
上記著書によると、0から1の範囲の一様乱数を12個ずつ足して6引くと、標準偏差1のガウス分布に近いな乱数が出せるようです。 関連する内容のブログを以前に書いていたので、一応載せておきます。 papiro.hatenablog.jp
Q6
これは文字を一つずつズラしながら表示する処理が必要な問題。 まずファイルの中身から4文字を1文字ズラしながら出力する処理を考えてみます。
$ cat 仏説摩訶般若波羅蜜多心経 観自在菩薩行深般若波羅蜜多時。照見五蘊皆空。度一切苦厄。 舎利子。色不異空。空不異色。色即是空。空即是色。受想行識亦復如是。 (省略) 是無等等呪。能除一切苦。真実不虚故。説般若波羅蜜多呪。 即説呪曰。羯諦羯諦波羅羯諦波羅僧羯諦菩提薩婆訶。般若心経
出題者上田さんの解答はawk
のsubstr()
関数を使っておられました。
別の考え方としてフィールド区切り文字を変更するやり方をやってみます。
awk
のフィールド区切り文字を空文字で指定すると、
第一フィールド($1)が一文字目、第二フィールド($2)が二文字目を参照します。
つまり1文字ずつフィールド分割して処理が可能になります。
フィールド区切り文字の指定方法には、下記の2通りがあります。
# 1. awkの-Fオプションを使う。 $ cat 仏説摩訶般若波羅蜜多心経 | tr -d '\n' | awk -F '' '{for(i=1;i<=NF-3;i++)print $i$(i+1)$(i+2)$(i+3)}' 観自在菩 自在菩薩 在菩薩行 (省略) 訶。般若 。般若心 般若心経 # 2. 第二引数(つまり入力ファイル名)に var=val の形式を指定すると、変数への代入と解釈される。 awkのフィールド区切り文字は特殊変数FSで指定可能。 $ cat 仏説摩訶般若波羅蜜多心経 | tr -d '\n' | awk '{for(i=1;i<=NF-3;i++)print $i$(i+1)$(i+2)$(i+3)}' FS=
後は2つのファイルについて4文字ずつズラした出力を取り出し、 両方のファイルに含まれるものを選び出します。
Q7
a2 + b2がab + 1で割り切れる正の整数の組合せa,bの生成。
1から1000までの整数を使った解答例がこちらです。
乱数の発生はawk
のsrand()
関数とrand()
関数を利用しました。
$ yes | awk 'BEGIN{srand()}{print int(1000*rand())+1,int(1000*rand())+1}' 122 849 338 152 654 454 366 786 (省略)
後はawk
でa2+b2をab + 1で割った剰余がゼロの時のみ出力するパターンを使います。
$ yes | awk 'BEGIN{srand()}{print int(1000*rand())+1,int(1000*rand())+1}' | awk '!(($1^2+$2^2)%($1*$2+1))' 27 240 30 8 27 3 1 1 3 27 6 216
更に(a2+b2)/(ab+1)の計算も出力して確認します。
ポイントは2つ目のawk
でfflush()
関数を使っている事です。
fflush()
関数を使わない場合、バッファリングされてなかなか出力されません。
$ yes | awk 'BEGIN{srand()}{print int(1000*rand())+1,int(1000*rand())+1}' | awk '!(($1^2+$2^2)%($1*$2+1)){print;fflush()}' | awk '{print $1,$2,($1^2+$2^2)/($1*$2+1)}' 125 5 25 8 30 4 27 3 9 64 4 16 8 2 4 112 30 4
(a2+b2)/(ab+1)が正の整数の二乗になっていることの確認までやるには、出題者上田さんの解答例を参照してください。
出力結果を更にパイプで他のコマンドに渡す際ですが、出力のデータ量が少ないとバッファリングされてなかなか出力されない現象が起きる場合があります。出力時に明示的にバッファリングさせない関数やオプションがあるコマンドの例がこちらです。
awk
:fflush()
関数grep
:--line-buffered
オプションsed
:-u
,--unbuffered
オプション
Q8
前のデータの結果から次を出力し続ける計算。
この問題はawk
の出力時に小数点以下の桁を減らさないのがポイントだった。
sprintf()
関数を使う方法もあるが、awk
には数値出力時のフォーマットを指定するOFMTという特殊変数がある。
参加者の方がツイートされていた例を参考にしてみました。
twitter.comこういうやつか……
— くんすと (@kunst1080) 2018年11月3日
yes | head -10 | awk 'BEGIN{a=3.1;x=0.5}{x=a * x * (1-x); print x}'#シェル芸
こんな感じで100万行出力し、重複した数値が無いかを確認します。。
$ yes | awk 'BEGIN{OFMT="%.20f";a=3;x=0.5}{x=a * x * (1-x); print x}' 0.75000000000000000000 0.56250000000000000000 0.73828125000000000000 0.57966613769531250000 0.73095991951413452625 (省略) # 出力桁数が少ないと重複してしまう $ yes | awk 'BEGIN{OFMT="%.5f";a=3;x=0.5}{x=a * x * (1-x); print x}' | head -n 1000000 | sort | uniq -d 0.65862 0.65873 0.65880 0.65886 0.65891 # 桁数を増やすと重複しなかった $ yes | awk 'BEGIN{OFMT="%.20f";a=3;x=0.5}{x=a * x * (1-x); print x}' | head -n 1000000 | sort | uniq -d
これ一体何に使うの?と思ったんですが、カオスの研究で重要らしいです。 連続で重複しない数値をたくさん出す必要があるためらしいです。
bcで計算する方法もありました。
twitter.combcなら100万ちゃんといくね
— eban (@eban) 2018年11月3日
echo 'x=0.5;a=3.9;for(i=1;i<=1000000;i++){x;x=a*x*(1-x)}' | bc -l | sort -u | wc -l
1000000#シェル芸
終了後
勉強会で出てきた事例に関連のあるブログ記事を書いてたのを思い出しました。 福岡サテライトでは、休息の後で自作記事を紹介しながら内容の解説を解説させていただきました。
全体を通して
前回に続いて、今回も長崎から手練れのシェル芸人の方が遠路はるばる参加されました。
参加者の方がレベルアップされてきたと感じられるのは嬉しいですね。
grep
の-f
オプションについて解説したところ、次の問題で活用出来る!と教えていただけたり。
参加者の増減はあるが、参加者の方へのサポートはそれなりに効果は出てきたかな?と思ったのですが、
いやいや私のサポートというのはおこがましい気がしますね。参加者の方々が頑張られているからでしょう。