「第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'の部分に対応しています。

  1. grep -oで1行1文字に並べる
  2. awk '!a[$1]++'で重複した行を削除
  3. sed -rz 's/\n$//;s/\n/|/g'で正規表現を完成させる
    1. s/\n$//で最後の改行文字を削除
    2. 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.*//'で生成しています。その内容は次の通りです。

  1. w3m -dumpで「https://ja.wikipedia.org/wiki/元号一覧_(日本)」からHTMLソースではなく整形された状態で内容を読み込む
  2. awk '/元和1615/,/慶応1865/'で江戸時代の元号だけを抽出
  3. sed -r 's/^(江戸時代)? +//;s/1.*//'で元号以外の不要な部分を削除
    1. s/^(江戸時代)? +//で行頭から元号の直前までを削除
    2. 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, …」と文字列を生成した後、awkforループを使って「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

ガウス分布(正規分布)に従った乱数ということで、Rrnorm(<乱数の数>, 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文字での折り返しをsedgrepで行い、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を難読化しよう!

zshの設定ファイル.zshrczcompileでコンパイルして読めなくすることができます。これと同様にbashの設定ファイル.bashrcでも何らかの方法で難読化できないかという発表です。

いたずらとはいえ、他人のシェルの設定ファイルを勝手に書き替えるのはやめましょう。

Google Colaboratoryの間違った使い方

Google Colaboratoryとは、クラウド上で実行される無償のJupyter Notebook環境です。

機械学習の教育や研究に役立つことを目的として提供されていますが、Ubuntuがインストールされた仮想マシンであることから、シェル芸実行環境としても利用できるという発表です。

当然ながら、:(){ :|:& };:の実行は御法度です。

Docker コンテナをネスト(したい)

docker runする時、-vオプションでホスト側に/var/run/docker.sock、コンテナ側に/var/run/docker.sockを指定してボリュームをマウントすると、/var/run/docker.sockはUNIXドメインソケットであるため、コンテナ側からホスト側のDockerデーモンを操作できるようになります。

この手法を利用すると、Dockerコンテナで:(){ :|:& };:みたいなことができるという発表です。

何気なく思ったのですが、危険Docker芸以外の平和目的でこの手法を使う場合、UNIXドメインソケットである/var/run/docker.sockに対する書き込み権限に注意する必要があるかもしれません。

参考リンク:


Thanx:

Written on November 14, 2018