「第38回シ҈ェ҈ル҈芸҈勉҈強҈҈会 大阪サテライト」に参加しました
2018年11月3日に開催された「第38回シ҈ェ҈ル҈芸҈勉҈強҈҈会 大阪サテライト」に参加しました。
当日の朝は典型的な秋晴れで格好のシェル芸日和となりました。
午前の部:
シェルで文字コードに触れてみる その3
- 講師:
- 鳥海秀一(@hid_tori)さん(USP友の会)
シェル芸を通して文字コードに親しもうという主旨のこの講義も、第3回目を迎えました。
講義冒頭では、これまでの振り返りとして
文字コードは単なる整数値 扱う文字集合の違いやその他の理由により様々な文字コードが存在する
などのキーワードが提示され、その例として
$ echo シューワ | iconv -f utf-8 -t cp930 | iconv -f cp939 -t utf-8
ニャーン
というシェル芸が紹介されました。
本題としては、次のようにシフトJISならびにUnicodeに関する説明が行われました。
- シフトJISコードとは
- 区点番号とは
- ダメ文字問題
- Unicodeとは
- Unicodeの歴史
これらの内容の理解を助けるために、区点番号からシフトJISコードを算出するシェル芸
$ echo 1 63 | awk '{printf("%02X %02X\n", ($1-1)/2+($1<63?0x81:0xc1),$2+($1%2?$2<64?0x3f:0x40:0x9e))}'
81 7E
$ echo 1 64 | awk '{printf("%02X %02X\n", ($1-1)/2+($1<63?0x81:0xc1),$2+($1%2?$2<64?0x3f:0x40:0x9e))}'
81 80
や、UTF-8文字列から区点番号を求めるシェル芸
$ printf シェル芸勉強会午前の部(初心者向け) | iconv -f utf-8 -t eucjp | xxd -p -u | fold -2 | sed 's/$/-A0/' | { echo ibase=16; cat; } | bc | xargs -n2
5 23
5 7
5 75
23 61
42 57
22 15
18 81
24 65
33 16
4 46
41 84
1 42
29 73
31 20
28 52
24 94
4 17
1 43
など、正確さと簡潔さを兼ね備えた「これぞシェルワンライナー」ともいえる華麗なシェル芸が披露されました。
そのうち、ターミナルに日本語をIMEを使わずに入力するシェル芸人が出現するかもしれません。
午後の部:
- 問題と解答例:
- 動画:
A1:
$ echo 'jus共催 第38回҈҈҉҈҈҉シ҈҉ェ҈҉ル҈҉芸҈҉勉҈҉強҈҉会' | xxd -p | tr -d '\n' | sed 's/d28[89]//g' | xxd -p -r
jus共催 第38回シェル芸勉強会
文字列「jus共催 第38回҈҈҉҈҈҉シ҈҉ェ҈҉ル҈҉芸҈҉勉҈҉強҈҉会」を次のように16進ダンプにすると、
$ echo 'jus共催 第38回҈҈҉҈҈҉シ҈҉ェ҈҉ル҈҉芸҈҉勉҈҉強҈҉会' | xxd -p | tr -d '\n'
6a7573e585b1e582ac20e7acac3338e59b9ed288d288d289d288d288d289e382b7d288d289e382a7d288d289e383abd288d289e88ab8d288d289e58b89d288d289e5bcb7d288d289e4bc9a0a
のようになるので、「҈」および「҈」に相当する「d2888」と「d2889」をsed
で消去した後xxd
で16進ダンプから文字列に戻します。
問題自体の難度は高くなかったものの、解答を作成するためにゼロ幅文字をコピーアンドペーストするところで苦戦しました。
A2:
$ echo 不摂生 | grep -o . | grep -P $(grep -o . 仏説摩訶般若波羅蜜多心経 | awk '!a[$1]++' | sed -rz 's/\n$//;s/\n/|/g') -
不
生
検索対象の文字列(ここでは「不摂生」)をgrep -o
で1行1文字ずつに並べたものに対してgrep -P <Perl互換の正規表現>
で検索にかけます。
そして、次の手順でファイル仏説摩訶般若波羅蜜多心経
から正規表現を生成します。この手順はgrep -o . 仏説摩訶般若波羅蜜多心経 | awk '!a[$1]++' | sed -rz 's/\n$//;s/\n/|/g'
の部分に対応しています。
grep -o
で1行1文字に並べるawk '!a[$1]++'
で重複した行を削除sed -rz 's/\n$//;s/\n/|/g'
で正規表現を完成させるs/\n$//
で最後の改行文字を削除s/\n/|/g
で全ての改行文字を正規表現での選択肢「|」に置換
A3:
$ w3m -dump 'https://ja.wikipedia.org/wiki/元号一覧_(日本)' | awk '/元和1615/,/慶応1865/' | sed -r 's/^(江戸時代)? +//;s/1.*//' | grep -P $(sed -rz 's/\n$//;s/\n/|/g' edo) -
元和
元禄
享保
安政
並び替えに必要な元号のリストをw3m -dump 'https://ja.wikipedia.org/wiki/元号一覧_(日本)' | awk '/元和1615/,/慶応1865/' | sed -r 's/^(江戸時代)? +//;s/1.*//'
で生成しています。その内容は次の通りです。
w3m -dump
で「https://ja.wikipedia.org/wiki/元号一覧_(日本)」からHTMLソースではなく整形された状態で内容を読み込むawk '/元和1615/,/慶応1865/'
で江戸時代の元号だけを抽出sed -r 's/^(江戸時代)? +//;s/1.*//'
で元号以外の不要な部分を削除s/^(江戸時代)? +//
で行頭から元号の直前までを削除s/1.*//
で元号の直後から行末まで(元号が適用された期間)を削除
このようにして得られた元号のリストに対して、ファイルedo
から生成した正規表現にマッチする行をgrep -P
で抽出します。
生成する手順はA2で使った手法の流用です。なお、次のような正規表現が生成されます。
$ sed -rz 's/\n$//;s/\n/|/g' edo
元禄|安政|元和|享保
A4:
$ echo {A..Z} {A..Z}{A..Z} {A..Z}{A..Z}{A..Z} | awk '{for(i=1;i<=NF;i++)if($i=="XYZ")printf("%s は %5i 番目にあります\n",$i,i)}'
XYZ は 16900 番目にあります
bash
のブレース展開で「A, B, C, …, Z, AA, AB, AC, …, ZZ, AAA, AAB, …」と文字列を生成した後、awk
のfor
ループを使って「XYZ」が格納されているフィールドを探します。
おそらく、この問題が今回のシェル芸勉強会で最も易しかったと思います。
A5(宿題):
$ echo 'rnorm(100, mean = 0, sd = 10)' | R --slave --vanilla | sed -r 's/^ +\[[0-9]+\] +//;s/ +/\n/g'
25.835971896
1.965286707
7.765205029
18.002956730
-16.572565642
(...略...)
14.561883244
-6.981182189
5.242585821
12.465719240
-5.564848511
ガウス分布(正規分布)に従った乱数ということで、R
のrnorm(<乱数の数>, mean=<平均値>, sd=<標準偏差>)
関数を使いました。
ヒストグラムの出力については、時間内に解答できなかったので、改めて宿題として解いたのですが、echo '...' | R --slave --vanilla
よりもRscript -e '...'
のほうがR
をシェル芸で使う際に便利なことに気付きました。
そして、次のようにNostalgiRパッケージのnos.hist()
関数でヒストグラムをアスキーアートとしてプロットすることができます。なお、Rscript -e '...'
の様式でないと標準出力にプロットできないので注意してください。
$ Rscript -e 'library("NostalgiR");rns<-rnorm(100,mean=0,sd=10);print(rns);nos.hist(rns)' 2>/dev/null
[1] 8.870250743 -6.405568208 -10.926330240 -9.863046065 -9.719243431
[6] 6.699150235 -1.306073444 10.290626772 1.916705424 1.084170305
[11] 15.380253956 13.310923045 2.740781080 -2.042769085 -0.969004019
[16] -9.811199945 -8.366565212 2.813533749 1.294265070 -10.903220702
[21] 0.464825614 -0.112102382 0.507517766 -7.405523543 -7.371866298
[26] -7.901981240 4.197166514 8.667356821 13.056207221 1.245656839
[31] -23.798218731 -6.706212517 -24.824737684 -0.277514346 1.228654982
[36] 8.209893116 -19.290921629 8.072694491 1.401844500 0.746930795
[41] -6.761822805 0.004726965 -2.400582356 3.711273588 -0.871097444
[46] -5.965823703 3.053384908 10.679176743 -10.640877895 3.195805662
[51] 4.890003888 7.348530551 15.210805697 8.059659399 6.844816123
[56] 9.417027281 8.212379913 -1.492837588 -20.723453830 -4.189858070
[61] -25.939073524 0.301692170 -8.103367516 3.117118403 -18.272089179
[66] 16.318558649 2.785106200 -15.920272972 2.018265546 8.890891819
[71] -9.758905662 3.452862458 -11.212986484 2.386038493 6.776550408
[76] -15.325292239 0.901602980 9.385271346 12.125855054 -2.497148239
[81] -1.003652745 13.552691433 2.639978831 -1.036390157 5.037612715
[86] -1.290666093 2.466373839 12.797250842 4.891741215 -8.002852167
[91] -5.121244085 14.240982977 20.772027680 3.214338809 -3.504449422
[96] -13.992471995 -11.117989682 3.462907903 18.061675435 -7.354852425
30 +----------+---------+----------+---------+----------+----+
| o |
25 + o +
F | o |
r | o |
e 20 + o +
q | o o |
u 15 + o o o o +
e | o o o o |
n 10 + o o o o +
c | o o o o o |
y 5 + o o o o o o +
| o o o o o o o o o |
0 + o o o o o o o o o o o +
+----------+---------+----------+---------+----------+----+
-30 -20 -10 0 10 20
A6(宿題):
$ nkf myoho_shinji.txt | sed -rz 's/\n//g;s/$/\n/' | pee 'grep -o ....' 'sed "s/.//"|grep -o ....' 'sed "s/..//"|grep -o ....' 'sed "s/...//"|grep -o ....' | awk '!/。/&&!a[$0]++' | grep -f <(sed -rz 's/\n//g;s/$/\n/' 仏説摩訶般若波羅蜜多心経 | pee 'grep -o ....' 'sed "s/.//"|grep -o ....' 'sed "s/..//"|grep -o ....' 'sed "s/...//"|grep -o ....' | awk '!/。/&&!a[$0]++') | sort
阿耨多羅
眼耳鼻舌
究竟涅槃
三世諸仏
三藐三菩
耳鼻舌身
若波羅蜜
色声香味
声香味触
即説呪曰
多羅三藐
得阿耨多
般若波羅
鼻舌身意
亦復如是
羅三藐三
耨多羅三
藐三菩提
妙法蓮華経の文字列に対して「漢字で4文字連続」という条件でパターンマッチさせるには、検索用パターンの供給源となる般若心経と検索対象である妙法蓮華経の両経ともに、次のように漢字4文字を並べておく必要があります。
- 1番・2番・3番・4番・改行・(…略…)
- 2番・3番・4番・5番・改行・(…略…)
- 3番・4番・5番・6番・改行・(…略…)
- 4番・5番・6番・7番・改行・(…略…)
これを実際に処理するのが次のシェル芸です。
$ echo '観自在菩薩行深般若波羅蜜多時。照見五蘊皆空。度一切苦厄。' | pee 'grep -o ....' 'sed "s/.//"|grep -o ....' 'sed "s/..//"|grep -o ....' 'sed "s/...//"|grep -o ....' | awk '!/。/&&!a[$0]++'
観自在菩
薩行深般
若波羅蜜
見五蘊皆
自在菩薩
行深般若
波羅蜜多
五蘊皆空
在菩薩行
深般若波
羅蜜多時
度一切苦
菩薩行深
般若波羅
照見五蘊
一切苦厄
pee
で標準入力を分岐させ(この場合は4つ)、それぞれに対する先頭部の不要な文字の削除と4文字での折り返しをsed
とgrep
で行い、awk '!/。/&&!a[$0]++'
で「。」が含まれている行と文字列が重複している行を削除します。
このシェル芸を両経に適用し、妙法蓮華経から生成した漢字データに対して般若心経から生成した漢字データでパターンマッチを行い(grep -f
)、必要であれば最後にsort
します。
個人的にはこの問題が一番難しかったです。
A7(宿題):
$ echo {1..1000}" "{1..1000} | sed -r 's/[0-9]+ [0-9]+ /&\n/g' | awk '{a=$1;b=$2;if(a<=b&&(a**2+b**2)%(a*b+1)==0)printf "a=%d, b=%d, sqrt=%d\n",a,b,sqrt((a**2+b**2)/(a*b+1))}'
a=1, b=1, sqrt=1
a=2, b=8, sqrt=2
a=3, b=27, sqrt=3
a=4, b=64, sqrt=4
a=5, b=125, sqrt=5
a=6, b=216, sqrt=6
a=7, b=343, sqrt=7
a=8, b=30, sqrt=2
a=8, b=512, sqrt=8
a=9, b=729, sqrt=9
a=10, b=1000, sqrt=10
a=27, b=240, sqrt=3
a=30, b=112, sqrt=2
a=112, b=418, sqrt=2
bash
のブレース展開で「1 1」から「1000 1000」までの正の整数の対を生成します。
この状態だと改行が存在しないため、sed -r 's/[0-9]+ [0-9]+ /&\n/g'
で整数2つごとに改行文字を挿入します。
その後はawk
を使い、問題に示された条件に合致する行をprintf()
で整形して出力します。
A8(宿題):
$ echo 'x=.5;a=3.999;for(i=1;i<=1000000;i++){print x,"\n";x=a*x*(1-x)}' | bc -l | sort -nu | pee cat 'wc -l'
.0009995000625
.00099950006277421709
.00099950006690664740
.00099950006910320950
.00099950007706865377
.00099950012333730890
.00099950013530542697
.00099950016650212571
.00099950026380325870
.00099950028398482317
(...略...)
.99974999994458724156
.99974999994963641893
.99974999997397995679
.99974999998178498427
.99974999998477925912
.99974999999635510334
.99974999999834795880
.99974999999889751142
.99974999999993139428
.99975
1000000
計算にある程度の精度が必要なため、-l
オプションを付けたbc
を使います。なお、この場合の小数点以下の10進での有効桁数は20桁です。
重複した数を削除するためにsort -nu
を、その後得られたユニークな数値とその出力数を得るためにpee cat 'wc -l'
を使います。
なお、bc -l
とは出力される数値が異なるものの、数値のprint
文においての出力書式を指定する特殊変数OFMT
に「%.20f」を代入すれば、GNU AWKでも解答することができます。
$ awk 'BEGIN{OFMT="%.20f";x=0.5;a=3.999;for(i=1;i<=1000000;i++){print x;x=a*x*(1-x)}}' | sort -nu | pee cat 'wc -l'
0.00099950006249988998
0.00099950006251719645
0.00099950008272496615
0.00099950009328237006
0.00099950011187708589
0.00099950014173344677
0.00099950019564361216
0.00099950019660789442
0.00099950024337491653
0.00099950027688738450
(...略...)
0.99974999994636293277
0.99974999995474733705
0.99974999996644786648
0.99974999996668911795
0.99974999998017677338
0.99974999998764646492
0.99974999999229863246
0.99974999999493996405
0.99974999999999569766
0.99975000000000002753
1000000
ちなみに、この問題はロジスティック写像を題材とした問題とのことで、a
の値を3.569945…以上にしてカオスにすると正答になるというものです。
LT大会:
~/.bashrcを難読化しよう!
- 発表者: たいちょー(@xztaityozx_001)さん
zsh
の設定ファイル.zshrc
はzcompile
でコンパイルして読めなくすることができます。これと同様にbash
の設定ファイル.bashrc
でも何らかの方法で難読化できないかという発表です。
いたずらとはいえ、他人のシェルの設定ファイルを勝手に書き替えるのはやめましょう。
Google Colaboratoryの間違った使い方
- 発表者: MSR(@msr386)さん
Google Colaboratoryとは、クラウド上で実行される無償のJupyter Notebook環境です。
機械学習の教育や研究に役立つことを目的として提供されていますが、Ubuntuがインストールされた仮想マシンであることから、シェル芸実行環境としても利用できるという発表です。
当然ながら、:(){ :|:& };:
の実行は御法度です。
Docker コンテナをネスト(したい)
- 発表者: so(@3socha)さん
docker run
する時、-v
オプションでホスト側に/var/run/docker.sock
、コンテナ側に/var/run/docker.sock
を指定してボリュームをマウントすると、/var/run/docker.sock
はUNIXドメインソケットであるため、コンテナ側からホスト側のDockerデーモンを操作できるようになります。
この手法を利用すると、Dockerコンテナで:(){ :|:& };:
みたいなことができるという発表です。
何気なく思ったのですが、危険Docker芸以外の平和目的でこの手法を使う場合、UNIXドメインソケットである/var/run/docker.sock
に対する書き込み権限に注意する必要があるかもしれません。
参考リンク:
- jus共催 第38回シェル芸勉強会リンク集 | 上田ブログ
- jus共催 第38回҈҈҉҈҈҉シ҈҉ェ҈҉ル҈҉芸҈҉勉҈҉強҈҉会 - Togetter
- 第38回シェル芸勉強会@大阪サテライトに参加しました - たいちょーの雑記
Thanx:
- 大阪サテライト:
- 主催:
- so(@3socha)さん
- 小原一哉(@KoharaKazuya)さん
- 会場:
- 主催:
- メイン会場:
- 講師:
- 鳥海秀一(@hid_tori)さん
- 上田隆一(@ryuichiueda)さん
- 講師: