「第35回シェル芸勉強会 大阪サテライト」の復習

2018年4月7日に開催された「第35回シェル芸勉強会 大阪サテライト」に参加しました。

前回よりは花粉症の自覚症状がいくらかマシになりましたが、問題の難度が通常の酷さ+αになりましたので疲労度は今回の方が高かったです。

当日に解けなかった問題も多く、Q1から打ちのめされた回となりました(スラスラと解答している方々、凄いというか怖いです)。


シェル芸勉強会:

問題と解答例:

動画:

A1(宿題):

$ curl -m 10 parrot.live | less > parrot; perl -e 'open($FH,"<","parrot");while(<$FH>){if(/2J/){print($str);$str=$_;select undef,undef,undef,0.07}else{$str.=$_}}'
(...生け捕り開始...)
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 20570    0 20570    0     0  11204      0 --:--:--  0:00:01 --:--:-- 11203
(...生け捕り終了...)


                   .ccccccc.
               .ccckNKOOOOkdcc.
            .;;cc:ccccccc:,:c::,,.
         .c;:;.,cccllxOOOxlllc,;ol.
        .lkc,coxo:;oOOxooooooo;..:,
      .cdc.,dOOOc..cOd,.',,;'....':l.
      cNx'.lOOOOxlldOc..;lll;.....cO;
     ,do;,:dOOOOOOOOOl'':lll;..:d:''c,
     co..lOOOOOOOOOOOl'':lll;.'lOd,.cd.
     co.'dOOOOOOOOOOOo,.;llc,.,dOOc..dc
     co..lOOOOOOOOOOOOc.';:,..cOOOl..oc
   .,:;.'::lxOOOOOOOOOo:'...,:oOOOc.'dc
   ;Oc..cl'':lldOOOOOOOOdcclxOOOOx,.cd.
  .:;';lxl''''':lldOOOOOOOOOOOOOOc..oc
,dl,.'cooc:::,....,::coooooooooooc'.c:
cNo.................................oc

Q1から難しく、解けないまま時間切れとなったため、宿題として解くことにしました。

前半のcurllessを使った「生け捕り」の部分では、curl-m <秒数>のオプションを付けて実行することで10秒間のみキャプチャを行った後、データ内のエスケープシーケンスが削除されないようにlessに渡してからファイルparrotにリダイレクトしています。

後半は普通のPerlワンライナーです。ファイルparrotには画面再描画用のエスケープシーケンスとして「ESC[2J」という文字列がありますのでそれを手がかりとして処理を行います。

「2J」が含まれた行が現れるまで変数$strに行のデータを収めていき、「2J」が含まれた行に達する毎に変数$strのデータを出力します。

なお、sleep()関数では1秒未満の遅延に対応していないため、select()関数を使って0.07秒間のウェイトをかけています。

A2(宿題):

$ nkf -Z herohero | sed 's/^[0-9]\+/& /' | awk '{chr[$1]=$2;row=$1}END{for(i=1;i<=row;i++)print chr[i]}'
へ





ろ

へ



ろ

まず、ファイルheroheroの中身は次のようになっています。

$ cat herohero
1へ
7ろ
9へ
13ろ

数字が全角になっていますので、nkf -Zで半角にする必要があります。

これに、sed 's/^[0-9]\+/& /'で数字の後に「 」を挿入した後、awkに渡します。

第1フィールドの値(行番号)を添字に持つ配列chrの各要素に第2フィールドの値を代入し、また変数rowに行番号を上書きで代入します。ENDブロックでforループを1から変数rowの値(13)まで回し、配列chrの値を出力していけば解答のようになります。

A3(改良):

$ awk '{data[$1][$2]+=1}END{for(i in data){printf("\n%d",i);for(j in data[i])printf(" %s:%d",j,data[i][j])}printf("\n")}' data

1 A:2 B:3
2 A:1 C:3
3 B:3 C:1 D:1
4 C:1

当日の解答には無駄な処理がありましたので改良したものを挙げておきます。

第1フィールドの値(1〜4)と第2フィールドの値(A〜D)をキーに持つ二次元の連想配列dataの各要素にそれぞれの出現回数を加算し、ENDブロック内でforの二重ループを使って題意に沿った書式で出力します。

A4:

$ w3m -dump 'https://namegen.jp/?sex=male&country=japan&lastname_cond=fukumu&lastname_rarity_cond=ika&firstname_cond=fukumu&firstname_rarity_cond=ika&middlename_cond=fukumu&middlename_rarity_cond=ika' | sed '29,89!d' | awk '!/^\[/{print $2}'
わたなべみのる
きたがわつばさ
おおかわまさひろ
ゆあさあきひろ
うらたひろゆき
おおたきしんいち
まつだいらりょう
こむらまさし
ひらこだいじろう
みすあきひろ
(...略...)
しせきてつあき
しまみやよしき
ごんないたかし
むろしまひさと
ざいかわながまさ
こがみひろあき
とみなかふみのり
みつしろくに
みねはらまさのり
うつやまふみひさ

すごい名前生成器 - 創作・ゲームに使えるランダム人名ジェネレータ」のページをw3mで取り込み、sedawkで不要な部分を取り除いて表示すればOKです。

A5(宿題):

$ echo 響け!ユーフォニアム | awk '{len=length($1)/2;for(i=0;i<len;i++){L=substr($1,1,len-i);R=substr($1,len+1+i);str[i]=L""R}}END{for(i=0;i<=len;i++){SP="";for(j=0;j<i;j++){SP=SP" "}print SP""str[i]}for(i=len;i>=0;i--){SP="";for(j=i;j>0;j--){SP=SP" "}printf SP;system("echo "str[i]"|rev")}}'
響け!ユーフォニアム
 響け!ユォニアム
  響け!ニアム
   響けアム
    響ム
     
     
    ム響
   ムアけ響
  ムアニ!け響
 ムアニォユ!け響
ムアニォフーユ!け響

宿題として解いてみましたが、awkコマンド1回で片付いた代わりに結構手間がかかる解答になりました。

文字列「響け!ユーフォニアム」を左右2つに分割し、それぞれについてforループ内のsubstr()で必要な文字のみ抽出した後、それらを結合した文字列を配列strの各要素に代入します。

その後、ENDブロックで出力の上半分と下半分をそれぞれforループで必要な分の「 」を先頭に挿入しながら出力します。

なお、下半分の出力についてはsystem("echo "str[i]"|rev")とすることで左右を反転しています。

A6:

$ seq 100000 | factor | awk '$NF<=23{sub(/:$/,"",$1);print $1}' | awk 'NR<=1985'
1
2
3
4
5
6
7
8
9
10
(...略...)
19950
19965
19968
19992
20000
20007
20020
20064
20102
20111

今回のシェル芸勉強会で最も易しいと感じた問題です(難度の基準がおかしい)。

factorによる素因数分解の結果に対し、次のawkで最終フィールド($NF)の値が23以上の行を抽出すれば簡単(?)に解答を導き出すことができます。

A7-1(別解):

$ seq 25 | ruby -r 'prime' -nle 'BEGIN{STR="💩うんこもりもり💩";idx=0};Prime.prime?($_.to_i)?(puts STR[idx];idx+=1):(puts [*"ぁ".."ん"].sample)'
ぢ
💩
う
す
ん
の
こ
と
る
を
も
り
り
ゎ
う
ぁ
も
む
り
ぴ
せ
し
💩
ざ
よ

当日の解答では素数番目でない文字に対して全て同じ文字を使いましたが、少し凝ってそれぞれランダムな平仮名が入るようにしてみました。

RubyのPrimeライブラリにある素数判定用のprime?()メソッドを使い、素数の行である場合は変数STRから1文字ずつ、そうでない場合は「ぁ」〜「ん」からランダムに1文字を出力します。

A7-2(宿題):

$ cat Q7-1.txt | awk '{printf $1" ";system("echo "NR"|factor")}' | awk 'NF==3{printf $1}END{printf "\n"}'
💩うんこもりもり💩

当日に解答し忘れましたので宿題として解答しました。A7-1に比べれば易しいです。

最初のawkで、読み込んでいる現在の行番号(NR)に対しsystem()関数によってfactorを適用します。

次のawkで、フィールド数(NF)が3つ、すなわち行番号が素数の行のみ第1フィールドに格納された文字を出力します。

A8:

$ cat a | factor | grep -P ':( \d)(\1){3}$' | awk '{sub(/:$/,"",$1);print $1}' | xargs | awk '{print $1"*"$2"*"$3"*"$4"="$1*$2*$3*$4}'
16*81*625*2401=1944810000

問題文には

ある自然数の4乗になっている組み合わせを1個以上

とありますので、ある自然数の4乗になっている数を4つ以上ファイルaから見つけ出すことが出来れば良いことになります。

$ cat a | factor | grep -P ':( \d)(\1){3}$'
16: 2 2 2 2
81: 3 3 3 3
625: 5 5 5 5
2401: 7 7 7 7

このように、ある自然数の4乗になっている数が4つありますので、これらの数を掛け算すればOKです。

ちなみに、掛け算の結果である1944810000を素因数分解すると次のようになります。

$ echo 1944810000 | factor
1944810000: 2 2 2 2 3 3 3 3 5 5 5 5 7 7 7 7

LT大会:

超・記号オンリー難読化シェル芸

数字と記号だけのシェル芸という変態極まりない発表です。dateコマンドの例がまるでBrainf*ckのソースコードのようです。

Dockerを使ったクライアントハイパーバイザー

第33回シェル芸勉強会 大阪サテライト」における同名のLTの続編です。実装方法等の詳細については、同名のスライドにて知ることができます。

デスクトップ環境が動くだけでなく音声もちゃんと出るようになっているのが何気に凄いです。

less でプレゼン

スライド作成ツール「Marp」用のMarkdownファイルを、ターミナル上でスライドショー化してしまうという発表です。

自分もスライドの作成にMarpを使っていますが、分割したMarkdownファイルをlessで表示していく手法は思い付きもしませんでした。

Google AIY Voice Kit

スマートスピーカーAIY Voice Kitをハックして:(){ :|:& };:が実行できる危険シェル芸アシスタントに仕立て上げるという発表です。

…OK Google! DSA (Dangerous Shell Arts)!

yesコマンドは速くなっている

シェル芸人お気に入りのコマンドの一つであるyesについて、高速化の歴史とその仕組みを解説しています。

grepもそうですが、GNU版のコマンドの実行スピードは速いです。


参考リンク:


Thanx:

Written on April 18, 2018