たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

第54回シェル芸勉強会に参加しました

参加しました

今回もリモート開催でした。

配信のURLはこちらです。

明日はちょっと時間とれなさそうなので、今回は開催当日の投稿です。というわけで時間中に解答できたものだけ記述します。PowerShell解はありません。

Q1

「おらおら」とか「へいへい」みたいな (..)\1にマッチしそうな文字列がちょうど2個含まれる行を行番号とともに出力する問題。おらおら!

$ cat Sh*/*54/oraora.txt | nl | while read R L; do [[ $(grep -oPe "(..)\1" <<< $L | sort | uniq -c | awk '$0=$1' | jq -s add ) == 2 ]] && echo $R $L ; done

nlでとりあえず行番号を付与し、while readの力業です。ここではRに行番号、Lに対象の文字列が入るので、条件にあう文字列かどうかを評価してechoするかどうかを決めています。なんか解答みてたらつよい正規表現が出てきてすごいなと思いました(こなみ)

Q2

偶数行にが書かれているファイルkouun.txtで、矢印の向いている奇数行を出力する問題。どういうことかというと

うんこ
↑

これはが「うんこ」を指しているので「うんこ」を出力する

これは
↑
シェル芸
↓
勉強会

これだと「シェル芸」は指されてないので出力しないといった具合。やってみます

$ cat Sh*/*54/kouun.txt | pee "grep -B1 ↑" "grep -A1 ↓" | grep -vPe "↑|↓|--" | sort -u

grepABはマッチした行のN行前後を出力するオプション。ならそのひとつ前。ならそのひとつ後を取り出すようにします。これを同時にやりたいので今回はmoreutilsのpeeを使いました。ただ、これだと順番は無視されちゃいますね

Q3

human.txtにある絵文字を解析して、男性を表す(MAN|BOY)な絵文字の数、女性を表す(GIRL|WOMAN)な絵文字の数をそれぞれ数え上げる問題。

$ cat She*/*54/human.txt | uni i | sort | uniq -c | awk '$10~/^(BOY|MAN)$/{a+=$1}$10~/^(GIRL|WOMAN)$/{b+=$1}END{print "男:"a, "女:"b}'

家族の絵文字は男性や男の子、女性、女の子といった絵文字をZWJで連結することで表現されます。なのでuninameのような文字のユニコード情報を返すコマンドを使えばあとは集計するだけですね。ここではuni

https://github.com/arp242/uni

Q4

yoko.txtのうちあ行の文字の個数、か行の文字の個数、さ行の文字の個数・・・・わ行の文字の個数をそれぞれ集計する問題。

$ cat She*/*54/yoko.txt | grep -o . | uconv -x latin | sed -E 's/^.$/a&/;s/^(.).+/\1/;y/aksctnhmyrw/あかさたたなはまやらわ/' | sort | uniq -c | sort -r

uconv -x latinを使うことでローマ字読みに変換できます。あとは子音をあかさたなはまやらわに置換してやれば sort | uniq -c | sortで集計ができますね。

Q5

小問1

Figure1Figure2_032321.fastaというファイルから、一番長い同じ文字が連続する文字列とそれが含まれる行の行番号を出力する問題。

$ grep -nEo -e{A,G,T,C}+ ./Figure1Figure2_032321.fasta | awk -F: '{print $0, length($2)}' | sort -nrk2 | head -n1
106:CCCCCC 6

小問2

同ファイルの改行を無視したときの場合を出力する問題。こっちは無理だった。

Q6

jugem.txtを以下のような形に変形して下さいという問題

寿水行ころパイーリンポ
限の末とやポポグンポコ
無魚雲むぶイののダのナ
寿水来住らパシンイーの
限利末にこポュガグピ長
無砂風ろうイーンーコ久
五海来こじパリリリポ命
劫れ末とのじンーンンの
のき食るぶうガュダポ長
すりう寝らこンシイの助

縦書きっぽいけど、蛇腹のようになっています。これは…teipの出番っぽいですね。

$ cat She*/*54/jugem.txt | grep -oPe .{10} | teip -l 2,4,6,8,10 -- rev | sed 's/./& /g' | rs -T | tr -d ' '
寿水行ころパイーリンポ
限の末とやポポグンポコ
無魚雲むぶイののダのナ
寿水来住らパシンイーの
限利末にこポュガグピ長
無砂風ろうイーンーコ久
五海来こじパリリリポ命
劫れ末とのじンーンンの
のき食るぶうガュダポ長
すりう寝らこンシイの助

teipで偶数行だけrev、あとは転置するだけですね。

Q7

nejineji.txtは以下のようなファイルです

寿限無寿限無五劫のすりきれ
            海
じのぶらこうじパイポパ 砂
う         イ 利
こ グーリンダイグ ポ 水
ら の     ー パ 魚
ぶ ン コピー リ イ の
や ガ ポ   ン ポ 水
ろ ン ンポのイダ の 行
こ リ       シ 末
と ーュシンガンリーュ 雲
む           来
住にろことる寝う食末来風末

これを一行に直してくださいという問題。ヤダ!!!!!!!!!!!!!!!!!

$ A="$(cat Sh*/*54/nejineji.txt|sed 's/./& /g')";while [[ "$A" != "" ]]; do echo $A|head -n1;A="$(echo $A|tail -n+2|rs -T|tac)";done | sed -zEe 's/ | |\n//g'

問題のnejineji.txtをよく観察してみると文字がぐるぐる巻きになっていることがわかりますね。というわけで1行目を取り出して回転、1行目を取り出して回転、を繰り返せばよさそうです。とりあえず試してみましょう

一行目の取り出しは以下でいいでしょう

$ head -n1 ./nejineji.txt
寿限無寿限無五劫のすりきれ

そして2行目以降を転置させてみます。各文字を要素としたいのでsedで空白区切りに変えておきます。

$ tail -n+2 ./nejineji.txt | sed 's/./& /g' | rs -T 
   じ  う  こ  ら  ぶ  や  ろ  こ  と  む  住
   の                             に
   ぶ     グ  の  ン  ガ  ン  リ  ー     ろ
   ら     ー                 ュ     こ
   こ     リ     コ  ポ  ン     シ     と
   う     ン     ピ     ポ     ン     る
   じ     ダ     ー     の     ガ     寝
   パ     イ           イ     ン     う
   イ     グ  ー  リ  ン  ダ     リ     食
   ポ                       ー     末
   パ  イ  ポ  パ  イ  ポ  の  シ  ュ     来
                                 風
海  砂  利  水  魚  の  水  行  末  雲  来  末

rs -Tで転置をすると次に取り出したい行が最後尾に来ることがわかります。これをtacコマンドを使って上下を反転すれば…

$ tail -n+2 ./nejineji.txt | sed 's/./& /g' | rs -T | tac
海  砂  利  水  魚  の  水  行  末  雲  来  末
                                 風
   パ  イ  ポ  パ  イ  ポ  の  シ  ュ     来
   ポ                       ー     末
   イ     グ  ー  リ  ン  ダ     リ     食
   パ     イ           イ     ン     う
   じ     ダ     ー     の     ガ     寝
   う     ン     ピ     ポ     ン     る
   こ     リ     コ  ポ  ン     シ     と
   ら     ー                 ュ     こ
   ぶ     グ  の  ン  ガ  ン  リ  ー     ろ
   の                             に
   じ  う  こ  ら  ぶ  や  ろ  こ  と  む  住

こうなります。ここで気付いてほしいのは、これが最初の状態から90度回転させた状態になっていることです。ということは、これを繰り返せば最終的には1行になりそうですね。

ではこれを繰り返すような処理を書いていきます。ここでは読みやすいように改行した状態で示します

$ A="$(cat nejineji.txt|sed 's/./& /g')";
$ while [[ "$A" != "" ]]; do 
  echo $A | head -n1
  A="$(echo $A|tail -n+2|rs -T|tac)"
done
寿 限 無 寿 限 無 五 劫 の す り き れ
海  砂  利  水  魚  の  水  行  末  雲  来  末
風  来  末  食  う  寝  る  と  こ  ろ  に  住
む  と  こ  ろ  や  ぶ  ら  こ  う  じ   
                               
                            
                            
                        の
ぶ  ら  こ  う  じ  パ  イ  ポ  パ
イ  ポ  パ  イ  ポ  の  シ  ュ
ー  リ  ン  ガ  ン  シ  ュ  ー
リ  ン  ガ  ン  の  グ   
                   
                
                
            ー
リ  ン  ダ  イ  グ
ー  リ  ン  ダ
イ  の  ポ  ン
ポ  コ   
       
    
    
ピ
ー

おお~。いいですね。あとは余計な改行や空白を削除しましょう。

$ A="$(sed 's/./& /g' nejineji.txt)"; while [[ "$A" != "" ]]; do head -n1 <<< $A; A="$(tail -n+2 <<< $A|rs -T|tac)"; done | sed -zEe 's/ | |\n//g'
寿限無寿限無五劫のすりきれ海砂利水魚の水行末雲来末風来末食う寝るところに住むところやぶらこうじのぶらこうじパイポパイポパイポのシューリンガンシューリンガンのグーリンダイグーリンダイのポンポコピー

できました。とんでもない問題でした。

再帰を使って書いてみたのが以下です。やっていること自体は同じです。気が向いたら読んでみてください。

$ a(){ A="$(cat)"; [[ "$A" == "" ]] && return; head -n1 <<< $A; tail -n+2 <<< $A | rs -T | tac | a }; sed 's/./& /g' nejineji.txt | a | sed -Ez 's/ | |\n//g'

この解答だと zshでしか動かないかもなので、bashじゃないとヤダよ!って人はいい感じに書き換えてくださいね

おわり

今回も楽しかったです。Q7は考えすぎのせいかわからないですが体温が高くなっているのを感じました。おそろしいね。 やはりTLに投稿された解答をみて「こういうのもあるのか」と思うのが楽しい部分の一つですね。今回もそれができたのがよかったです。

企画、開催ありがとうございました!

| ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄|
|        終        |
|    制作・著作    |
|   ̄ ̄ ̄ ̄ ̄ ̄ ̄  |
|    xztaityozx    |
|_________|
     ∧∧ ||      
     ( ゚д゚)||      
    /   づΦ     

雑記 2021/06/20

雑記だよ

そうだよ

Twitterが健康に悪い

主に精神面だがTwitterが健康に悪い。負をツイートしたり負をリプしたり負をRTしたりしているのを見るといやな気持ちが流れ込んできてへこむ。全然知らないやつのメンヘラツイートに影響される自分にもへこむ。なのでTwitterを見る代わりに運動するようにした。いまのところ調子がいい。 でもプリキュアの時間だけは実況しちゃう。意味なし

ハードシューター目指そうと思った

東方の難易度Hardをクリアできるようになりたいなと思ったので少しずつ挑戦している。現在の進捗は妖神天獣虹といったところ。まだまだだけどがんばる

おわり

運動してテンション上がったノリで書き始めたけど、思ったより書くことなかったな

第53回シェル芸勉強会に参加しました

今回も参加しました。リモートになってから1年ぐらい経ったような…。

YouTubeアーカイブここから見られます。

Q1

まずは準備として以下のコマンドを実行します

$ seq 1e6 | shuf > a

1~100万までの数値をランダムで並び変えたファイルaを作る。 このファイルの1行目の数値と5行先の数値を足して、2列目に出力してくださいという問題

例えば

1
3
5
7
9
11

だとすると

1 12 <= 1 + 11
3 a  <= 3 + b
5 c  <= 5 + d
...

なるほど。これは先に先頭5行を削除したファイルをpasteすると楽かもですね。やってみます

$ seq 1e6 | shuf > a; paste a <(awk 'NR==6,0' a) | awk 'NF==2{print $1, $1 + $2}'

いいですね。先頭n行を削除する。いろんな方法があるかと思いますが今回はawktail -n +6とかでもいいと思います

Q2

fracファイルは1列目、2列目に数値が書いてあります。これを約分してください。ただし1列目が分子、2列目が分母とします

やくぶん!?と思ったけどfactorで共通部分を消せばいいかなということで

$ cat Sh*/vol.53/frac | factor | awk 'NR==1{for(i=2;i<=NF;i++){b[$i]++}}NR==2{for(i=2;i<=NF;i++)b[$i]--}END{s=1;t=1;for(x in b) if(b[x]>0){s*=x}else if(b[x]<0){t*=x}; print s, t}'
568396410 91

分母、分子のfactorの結果を分母ではそれぞれ数え上げ、分子では引き算する。これにより個数が正なら分母の、個数が負数なら分子の残りだとわかるので、あとはこれをかけ合わせればよいということですね。

ちなみに

$ cat Sh*/vol.53/frac | awk '{print "p "$1"/"$2"r"}' | ruby
(568396410/91)

rubyなら末尾のr有理数を表せるので簡単に計算できる。いいなあこれ。

Q3

numsファイルに記述してある数値を最後の桁で四捨五入する問題。つらい

$ cat She*/vol.53/nums | awk '{print "%."length($0)-4"f\
", $0}'  | xargs -n2 printf

printfで四捨五入ですね。

Q4

以下の手続きをとると、必ずある数値に収束します。それを求めてください

  • 4桁以下のゾロ目でない数aを適当に選び、3桁以下なら頭に0を足して4桁に
  • aの各桁を数字が大きい順にソートしてb、小さい順にソートしてcを作成
  • a=b-cとして2に戻る
$ a=4890; while true; do [[ ${#a} == 3 ]] && a=0$a; b=$(grep -o . <<< $a|sort -nr|tr -d \n); c=$(grep -o . <<< $a|sort -n|tr -d \n);d=$((b-c)); [[ $a == $d ]] && break; a=$d;done; echo $a
6174

6174に収束しました。手続き通りにワンライナーを書いているだけです。桁のソートは

$ grep -o . <<< $a|sort -nr|tr -d \n
$ grep -o . <<< $a|sort -n|tr -d \n

でやっています。横方向のソートが欲しくなりますねえ

Q5

ある範囲のふたつの素数をランダムに選んで、6要素の等差数列を作ってください。という問題。例えばランダムに選んだ二つの素数が11と31だとすると、その差は20なので以下のような等差数列を生成するというもの

11 31 51 71 91 111

awkでえい

$ seq 1e4 | factor | awk 'NF==2{print $2}' | shuf | xargs -n2 | awk 'NF==2{if($1>$2){a=$2;$2=$1;$1=a}print}' | awk '{a=$2;d=$2-$1;for(i=3;i<=6;i++){$i=a+=d};print}' | head

先のawkで大小を入れ替え、後のawkでデータを作ります。$iにどんどん代入していって、フィールドを作っています

Q6

Q5のデータをもとに等差数列すべてが素数な行を出力する問題。なんとなくむずそうに聞こえるのだけど落ち着いて行ごとに処理してみます

$ cat a | awk '{print "echo ",$0, ":  $(openssl prime ",$0}' | sed "s/\$/|awk '{printf NF==4}')/" |bash | awk '$NF=="111111"'

はい…これはちょっと解説しようと思います。まずは最初のawkの出力を見てみます。

$ cat a | awk '{print "echo ",$0, ":  $(openssl prime ",$0}'
echo  1601 3727 5853 7979 10105 12231 :  $(openssl prime  1601 3727 5853 7979 10105 12231
echo  3019 7057 11095 15133 19171 23209 :  $(openssl prime  3019 7057 11095 15133 19171 23209
echo  127 1901 3675 5449 7223 8997 :  $(openssl prime  127 1901 3675 5449 7223 8997
echo  6911 9547 12183 14819 17455 20091 :  $(openssl prime  6911 9547 12183 14819 17455 20091
echo  107 8539 16971 25403 33835 42267 :  $(openssl prime  107 8539 16971 25403 33835 42267
echo  3373 8443 13513 18583 23653 28723 :  $(openssl prime  3373 8443 13513 18583 23653 28723
echo  3307 4159 5011 5863 6715 7567 :  $(openssl prime  3307 4159 5011 5863 6715 7567
echo  809 1997 3185 4373 5561 6749 :  $(openssl prime  809 1997 3185 4373 5561 6749
echo  1597 1747 1897 2047 2197 2347 :  $(openssl prime  1597 1747 1897 2047 2197 2347
echo  4057 9887 15717 21547 27377 33207 :  $(openssl prime  4057 9887 15717 21547 27377 33207

中途半端なワンライナーが出力されます。後段のsedも見てみましょう

$ cat a | awk '{print "echo ",$0, ":  $(openssl prime ",$0}' | sed "s/\
$/|awk '{printf NF==4}')/"
echo  1601 3727 5853 7979 10105 12231 :  $(openssl prime  1601 3727 5853 7979 10105 12231|awk '{printf NF==4}')
echo  3019 7057 11095 15133 19171 23209 :  $(openssl prime  3019 7057 11095 15133 19171 23209|awk '{printf NF==4}')
echo  127 1901 3675 5449 7223 8997 :  $(openssl prime  127 1901 3675 5449 7223 8997|awk '{printf NF==4}')
echo  6911 9547 12183 14819 17455 20091 :  $(openssl prime  6911 9547 12183 14819 17455 20091|awk '{printf NF==4}')
echo  107 8539 16971 25403 33835 42267 :  $(openssl prime  107 8539 16971 25403 33835 42267|awk '{printf NF==4}')
echo  3373 8443 13513 18583 23653 28723 :  $(openssl prime  3373 8443 13513 18583 23653 28723|awk '{printf NF==4}')
echo  3307 4159 5011 5863 6715 7567 :  $(openssl prime  3307 4159 5011 5863 6715 7567|awk '{printf NF==4}')
echo  809 1997 3185 4373 5561 6749 :  $(openssl prime  809 1997 3185 4373 5561 6749|awk '{printf NF==4}')
echo  1597 1747 1897 2047 2197 2347 :  $(openssl prime  1597 1747 1897 2047 2197 2347|awk '{printf NF==4}')
echo  4057 9887 15717 21547 27377 33207 :  $(openssl prime  4057 9887 15717 21547 27377 33207|awk '{printf NF==4}')

openssl primeを使うと、その数値が素数かどうかを判定して返してくれます。その出力を見てみましょう

$ openssl prime 1601 3727 5853 7979 10105 12231
641 (1601) is prime
E8F (3727) is prime
16DD (5853) is not prime
1F2B (7979) is not prime
2779 (10105) is not prime
2FC7 (12231) is not prime

注目したいのは列の数です。素数じゃないときは not が存在するので、列の数が5になります。反対に素数の時は4です。

なのでこれを以下のようなawkに渡すと

awk '{printf NF==4}'

素数かどうかを0,1で表すことができます。このとき改行なしで出力されるので、6個のデータは1列にまとまります。というわけですべて素数の場合は 111111 という文字列が得られるはずです

あとはこれをgrepなりすれば良いですね。ただしデータの方に 111111 が含まれる可能性があるので、ここでもawkを使って処理しています

とまぁここまで頑張って書きましたが、これteipで以下のように書けます

$ cat a | teip -f 1-6 factor | awk 'NF==12' | sed 's/[0-9]+: //g'

素晴らしい・・・。横方向への部分適用はteipが便利ですね。出力が横に並ぶことを知らなかったので次からは使えるといいですね

Q7

力尽きました。

LT

今回はLTさせていただきました。ゲームなどにある「実績システム」をシェルに導入したらふとした時にうれしいよねということで実装したという話をしました。

www.slideshare.net

おわり

日常でシェル芸を書いていても達成感とかはまあないのですが、シェル芸勉強会では解けると嬉しいのでいいですよね。解いた後ほかの人の解答を眺めたり、試したりする時間も楽しいですし。

今回も開催ありがとうございました!

第52回シェル芸勉強会に参加しました

参加しました

今回は声を出す参加者を募集してYouTubeで配信というスタイルだったようです。会場のうめき声を思い出させるような感じになるかと思いましたが、最後の方は無言になってましたね…そういえば会場の時もうめき声というよりは無言だったような…

さて今回も最中に解けたものに関して解答を残しておきます。先にネタバレをしておきますが、今回は最後の数問が激ムズでした。

問題と解答はここYouTubeの配信はここです

Q1

big_dipperファイルにはAAで北斗七星が書いてあります。

$ cat big_dipper
*

      *

      *

       *

   *


             *
       *

ほんとだ・・・。さてこれの*に「チキチキボーン」を順番に当てはめてくださいという問題。早速やってみます。

$ cat ./Sh*/v*52/chiki | grep -o . | xargs -I@ echo '0,/\*/ s/\*/@/' | sed -f- ./Sh*/v*52/b*
チ

      キ

      チ

       キ

   ボ


             ー
       ン

これがかの有名なチキチキボーン七星か。さて解答についてですが、まずはxargssedの命令を生成していきます。

$ cat ./Sh*/v*52/chiki | grep -o . | xargs -I@ echo '0,/\*/ s/\*/@/'
0,/\*/ s/\*/チ/
0,/\*/ s/\*/キ/
0,/\*/ s/\*/チ/
0,/\*/ s/\*/キ/
0,/\*/ s/\*/ボ/
0,/\*/ s/\*/ー/
0,/\*/ s/\*/ン/

最初にマッチした*だけを置換してほしいので、0,/\*/で置換範囲を制限しています。あとはこれをsedに与えればよいですね。

PowerShellでの解答。こちらもsedを使うのが楽なので、sedを使います。ということで、上の命令を生成するところまでやってみましょう

$ (cat .\chiki -Encoding UTF8) -split '' | ?{$_} | %{"0,/\*/ s/\*/$_/"}
0,/\*/ s/\*/チ/
0,/\*/ s/\*/キ/
0,/\*/ s/\*/チ/
0,/\*/ s/\*/キ/
0,/\*/ s/\*/ボ/
0,/\*/ s/\*/ー/
0,/\*/ s/\*/ン/

特に難しそうなところはないですね。あえて取り上げるとしたら、?{$_}で空行を削除しているところですかね。

ところでチキチキボーンって何?

Q2

チキチキボーンを連続して出力してください。ただし文字は^の上かvの下に出力してください。^vupdownというファイルに記述されています。という問題

$ cat Sh*/vol.52/updown | grep -o . | paste - <(yes $(cat Sh*/vol.52/chiki)|head -n3|grep -o .|head -n20) | awk '$1 == "^"{print $2" ^  "}$1 == "v"{print "  v "$2}' | rs -t | sed 's/  //g'
チ チキボー チキチキボ     キ  
^ v ^ ^ ^ ^ v ^ ^ ^ ^ ^ v v v v v ^ v v
 キ    ン     ーンチキチ ボー

横に展開するデータをシェル芸で作るのは難しいので、まずは縦で作ってから転地するという方法。たまにありますよね。

$ cat Sh*/vol.52/updown | grep -o . | paste - <(yes $(cat Sh*/vol.52/chiki)|head -n3|grep -o .|head -n20)
^       チ
v       キ
^       チ
^       キ
^       ボ
^       ー
v       ン
^       チ
^       キ
^       チ
^       キ
^       ボ
v       ー
v       ン
v       チ
v       キ
v       チ
^       キ
v       ボ
v       ー

まずはこんなのを作っておきます。そしてawkなりで、^なら左に、vなら右にチキチキボーンを並べてきます。成形のため、反対側には全角スペースを置いておくといいと思います

$ cat Sh*/vol.52/updown | grep -o . | paste - <(yes $(cat Sh*/vol.52/chiki)|head -n3|grep -o .|head -n20) | awk '$1 =
= "^"{print $2" ^  "}$1 == "v"{print "  v "$2}'
チ ^  
  v キ
チ ^  
キ ^  
ボ ^  
ー ^  
  v ン
チ ^  
キ ^  
チ ^  
キ ^  
ボ ^  
  v ー
  v ン
  v チ
  v キ
  v チ
キ ^  
  v ボ
  v ー

なんかそれっぽくなりましたね。これの縦と横を入れ替えれば目的の形になりそうですが、これにはtateyokors -Tを私はよく使います

$ cat Sh*/vol.52/updown | grep -o . | paste - <(yes $(cat Sh*/vol.52/chiki)|head -n3|grep -o .|head -n20) | awk '$1 =
= "^"{print $2" ^  "}$1 == "v"{print "  v "$2}' | rs -T | sed 's/  //g'
チ チキボー チキチキボ     キ  
^ v ^ ^ ^ ^ v ^ ^ ^ ^ ^ v v v v v ^ v v
 キ    ン     ーンチキチ ボー

最後のsedは成形用ですね。

今度はPowerShellでの解答です。

$ (cat .\updown) -split '' | ?{$_} |% -Begin { $a=$b=$i=0; $c=@("","",""); $chiki=(cat -Encoding UTF8 ./chiki) }  { $d=[int]($_ -eq "^"); $c[0]+=@(" ", $chiki[$i%7])[$d]; $c[1]+="$_ "; $c[2]+=@($chiki[$i++%7], " ")[$d] } -End { $c }
チ チキボー チキチキボ     キ  
^ v ^ ^ ^ ^ v ^ ^ ^ ^ ^ v v v v v ^ v v
 キ    ン     ーンチキチ ボー

そう…ですね…Foreach-Objectのごり押し…awkごり押しみたいなもんです。

Q3

出来るをできるにして、side-by-sideでdiffを表示する問題

自分はdiffの代わりにdeltaをよく使うのでそれでやりました。diff -yでも同じことができるとのこと。

github.com

$ delta -s <(sed 's/出来/でき/g' Sh*/vol.52/dekiru) ./Sh*/vol.52/dekiru 

Q4

aho.htmlから間違ってるタグを見つけてください

一発で見つける必要はなくて、使われているタグを集計して、目grepで間違いを探すというのがよさそうです。

$ cat Sh*/v*52/aho.html | grep -oPe "<[^ >]+>|</[^>]+>" | sort | uniq -c | sort -rn 
    269 </a>
    258 </span>
     54 </div>
     31 </p>
     29 <p>
     25 </li>
     23 <li>
     18 </code>
     17 </script>
     16 </pre>
     12 </ul>
     11 <ul>
     12 </ul>
     11 <ul>
     10 </i>
      9 </h2>
      8 <code>
      7 </h4>
      7 </h3>
      6 <pre>
      6 </aside>
      5 <script>
      3 <span>
      3 </button>
      2 <aside>
      2 </ins>
      2 </h5>
      2 </h1>
      1 <title>
      1 <hr>
      1 <head>
      1 <h1>
      1 <footer>
      1 <body>
      1 </title>
      1 </strong>
      1 </spin>
      1 </nav>
      1 </html>
      1 </head>
      1 </form>
      1 </footer>
      1 </body>

</spin>があるのでこれがおかしいですね。あとは場所を特定するだけです

$ cat Sh*/v*52/aho.html | grep --color=auto -n spin
293:<a class="sourceLine" id="cb10-2" title="2"><span class="fu">awk</spin> <span class="st">&#39;{t=2*3.14*$2/1000000000;c=cos(t)*5+5;s=sin(t)*10+13;</span></a>}

293行目とのことでした。

さて次はPowerShellですね。集計は以下のようにしてやります

$ cat .\aho.html | sls "<[^ >]+>|</[^>]+>" | %{$_.Matches.Value} | group | sort Count -Descending

Count Name                      Group
----- ----                      -----
  152 </span>                   {</span>, </span>, </span>, </span>...}
   69 </a>                      {</a>, </a>, </a>, </a>...}
   29 <p>                       {<p>, <p>, <p>, <p>...}
   24 </div>                    {</div>, </div>, </div>, </div>...}
   23 <li>                      {<li>, <li>, <li>, <li>...}
   14 </script>                 {</script>, </script>, </script>, </script>...}
   12 </ul>                     {</ul>, </ul>, </ul>, </ul>...}
   10 </i>                      {</i>, </i>, </i>, </i>...}
    8 </h2>                     {</h2>, </h2>, </h2>, </h2>...}
    7 </h3>                     {</h3>, </h3>, </h3>, </h3>...}
    6 </aside>                  {</aside>, </aside>, </aside>, </aside>...}
    6 <pre>                     {<pre>, <pre>, <pre>, <pre>...}
    6 <ul>                      {<ul>, <ul>, <ul>, <ul>...}
    4 <script>                  {<script>, <script>, <script>, <script>}
    4 </code>                   {</code>, </code>, </code>, </code>}
    3 </button>                 {</button>, </button>, </button>}
    2 </ins>                    {</ins>, </ins>}
    2 </p>                      {</p>, </p>}
    2 <aside>                   {<aside>, <aside>}
    2 </li>                     {</li>, </li>}
    1 </body>                   {</body>}
    1 </html>                   {</html>}
    1 <footer>                  {<footer>}
    1 </spin>                   {</spin>}
    1 <hr>                      {<hr>}
    1 </footer>                 {</footer>}
    1 <body>                    {<body>}
    1 </form>                   {</form>}
    1 <title>                   {<title>}
    1 </head>                   {</head>}
    1 </nav>                    {</nav>}
    1 </h1>                     {</h1>}
    1 </strong>                 {</strong>}
    1 <h1>                      {<h1>}
    1 <head>                    {<head>}

grep -oに相当するのが sls "pattern" | %{$_.Matches.Value}です。普段使いしていないので、すぐ忘れてしまう。

Q5

annotation.mdには脚注があります。これをTeX形式の\footnote{...}にしながら元の場所に埋め込んで下さいという問題。

激しそうだけど、sedの命令を作り上げるのが良さそうということで、早速やってみます

$ cat Sh*/v*52/annotation.md | tail -n4 | rargs -p "\[\^(.+)\]: (.+)" echo "s/([^^])\[\^{1}\]/\1\\\footnote{{2}}/" | sed -E -f- ./Sh*/v*52/annotation.md
# Aについて

A\footnote{Aの起源は室町時代に遡る。}は素晴らしい。
Aのに似たものとしてB[^about_b]が存在するが、やはりAには及ばない。
他方でAに匹敵すると言われるC\footnote{Cの起源は江戸時代に遡る。}が近年注目を集めているが、これについても触れたい。
CとはもともとはDを発展させたものであり、F[^abort_f]という別名もある。

[^about_a]: Aの起源は室町時代に遡る。
[^about_c]: Cの起源は江戸時代に遡る。
[^about_d]: Dの起源はわからない。
[^about_f]: Fはおいしい。

rargsを使って、sedの命令を作ります。生成されるのは以下の感じ

$ cat Sh*/v*52/annotation.md | tail -n4 | rargs -p "\[\^(.+)\]: (.+)" echo "s/([^^])\[\^{1}\]/\1\\\
footnote{{2}}/"
s/([^^])\[\^about_a\]/\1\\footnote{Aの起源は室町時代に遡る。}/
s/([^^])\[\^about_c\]/\1\\footnote{Cの起源は江戸時代に遡る。}/
s/([^^])\[\^about_d\]/\1\\footnote{Dの起源はわからない。}/
s/([^^])\[\^about_f\]/\1\\footnote{Fはおいしい。}/

エスケープだらけで意味判らねえなおい。簡単に意味を書くと「行頭でない[^about_hoge]を\footnote{HOGE}に置換する」です。\1で後方参照しているのは「行頭でない」の[^^]が一文字拾ってしまうからです。否定後読みとかならいらないのかな

PowerShellでも今回はsedに食わせるスクリプトを生成することにします。

$ cat .\annotation.md -Encoding UTF8 | sls -Pattern '^\[\^(about_.)\]: (.
+)' | % {"s/([^^])\[\^" + $_.Matches.Groups[1].Value + "\]/\1\\\footnote{" + $_.Matches.Groups[2].Value + "}/"}
s/([^^])\[\^about_a\]/\1\\\footnote{Aの起源は室町時代に遡る。}/
s/([^^])\[\^about_c\]/\1\\\footnote{Cの起源は江戸時代に遡る。}/
s/([^^])\[\^about_d\]/\1\\\footnote{Dの起源はわからない。}/
s/([^^])\[\^about_f\]/\1\\\footnote{Fはおいしい。}/

slsではグルーピングもできます。MatchInfoGroupsでグループが参照できるので、それを使ってスクリプトを生成します。

Q6

TeXでは\label{hoge}に対して\ref{hoge}で参照が取れる。contents.texの中から\labelがあるのに\refがない。またはその逆なものを検索してくださいという問題。

$ cat Sh*/v*52/contents.tex | grep -oPe "\\(ref|label)\{[^\{]+\}" | tr -d '\}' | sort -u |awk '{a[$2]=a[$2]" "$1;}END{for(x in a){print x, a[x]}}' FS=\{ | column -t | awk 'NF!=3{print $1}' | grep -f- -n Sh*/v*52/contents.tex 
19:\subsection{ロボットの姿勢と座標系}\label{sub:pose}
116:    \varphi_{c,t} \sim \mathcal{N}\left(\varphi_{c,t}^*,(3\pi/180)^2\right) \label{eq:phidist}\\
157:\ref{sub:noise}項でモデル化した雑音から計算する。
170:    \V{e}_{c,t,t'} = \V{x}_t' - \V{x}_t - \V{\mu}_{c,t,t'}\label{eq:e}
220:また、式(\ref{eq:unko})から、
242:    \end{bmatrix} \label{eq:e2}
319:また、\ref{sub:noise}項の定義では、遠いところでランドマークを

途中のawkで、以下のようなデータを作りました。

$ cat Sh*/v*52/contents.tex | grep -oPe "\\\(ref|label)\{[^\}]+\}" | tr -d '\\}' | sort -u |awk '{a[$2]=a[$2]" "$1;}END{for(x in a){print x, a[x]}}' FS=\{ | column -t
eq:ddist               label  ref
sub:fullslam           label  ref
sub:pose               label
fig:observation_noise  label  ref
eq:e2                  label
eq:unko                ref
fig:coordinate         label  ref
sub:noise              ref
eq:phidist             label
eq:psidist             label  ref
fig:two_poses          label  ref
fig:observation        label  ref
eq:e                   label

labelがあるかrefがあるかの表です。あとはカラムの個数が足りない(=どちらかが足りない)ものについて検索をすれば終わりという感じ。

PowerShellではちょっとPowerShell感を使って解いてみます

$ cat .\contents.tex -Encoding UTF8 | sls '\\(ref|label)\{([^}]+)\}' | %{ $_.matches.groups[2].value + "::" + $_.matches.groups[1].value  } | sort -Unique | group { ($_ -split "::")[0]  } | ?{$_.count -eq 1} | %{ sls -Pattern $_.name -Path .\contents.tex -Encoding UTF8  } | sort -Unique

contents.tex:116:       \varphi_{c,t} \sim \mathcal{N}\left(\varphi_{c,t}^*,(3\pi/180)^2\right) \label{eq:phidist}
\\
contents.tex:117:       \psi_{c,t} \sim \mathcal{N}\left(\psi_{c,t}^*,(3\pi/180)^2\right) \label{eq:psidist}
contents.tex:157:\ref{sub:noise}項でモデル化した雑音から計算する。
contents.tex:170:       \V{e}_{c,t,t'} = \V{x}_t' - \V{x}_t - \V{\mu}_{c,t,t'}\label{eq:e}
contents.tex:19:\subsection{ロボットの姿勢と座標系}\label{sub:pose}
contents.tex:220:また、式(\ref{eq:unko})から、
contents.tex:242:       \end{bmatrix} \label{eq:e2}
contents.tex:275:$\theta$方向の共分散であり、式(\ref{eq:ddist}-\ref{eq:psidist})
contents.tex:319:また、\ref{sub:noise}項の定義では、遠いところでランドマークを

方針自体は一緒です。違うのは中間データとして、以下のようなものを作って

> cat .\contents.tex -Encoding UTF8 | sls '\\(ref|label)\{([^}]+)\}' | %{ $_.matches.groups[2].value + "::" + $_.matches.groups[1].value } | sort -Unique
eq:ddist::label
eq:ddist::ref
eq:e::label
eq:e2::label
eq:phidist::label
eq:psidist::label
eq:unko::ref
fig:coordinate::label
fig:coordinate::ref
fig:observation::label
fig:observation::ref
fig:observation_noise::label
fig:observation_noise::ref
fig:two_poses::label
fig:two_poses::ref
sub:fullslam::label
sub:fullslam::ref
sub:noise::ref
sub:pose::label

これの各行に対し::splitした一個目をキーにGroup-Objectしています。こういった集計はやりやすくていいなあと思います。

Q7

$ cat Sh*/v*52/contents.tex | awk '/\\begin\{bmatrix\}/,/\\end\{bmatrix\}/{printf $0"@@@"}/\\end\{bmatrix\}/{print ""}' | awk '{print gsub("&","&"), $0}' | awk '$1==6{$1="";print}' | sed -z 's/@@@/
/g'

ほぼ先生の解答通り。awk '/begin/, /end/'を最近AWK処方箋で読んだので、使えてよかったです

Q8

重複するcontents.texには存在するので、その位置を出力する問題。ぜんっぜんわからなかった。

終わりに

今回はQ1から時間ギリギリにしかできなくてやばかったですね。sedスクリプトを生成するっていうのはいかにもシェル芸的なので楽しいですね。でも、あんまり普段はやらない作業かもなぁと思いました。文章の修正とかをする人ならよくあるのかな?って感じです。

今回もとても楽しかったです。企画・開催ありがとうございました!!

第51回シェル芸勉強会に参加しました

参加しました

今回もリモート開催ということでした。そろそろここの前置きに書くこともなくなってきました。

さて毎回の通りですが、時間中に出したzsh/bashでの解答と、これを書きながら考えたPowerShellでの解答の両方を載せていこうと思います。PowerShell全然わからないので難しい。

問題と解答はここから見られます。リンク先はYouTubeのリストで、各問題ごとに分けられています。いつもの問題と解答ページは更新されたらここに追記しようと思います。

Q1

pepoファイルに隠れたうんこを探し出す問題。出うんこ

これはまぁなんというかターミナルのせいだけど、探す前から若干うんこが見えてました

bash/zsh

$ cat pepo | sed "s/$(echo -e 'ほ\U309a'/(/g" | sed "s/$(echo -e 'へ\U309a'/)/g"|sed 's/[ぽ|ぺ]/  /g'

()が他の「ぽ」や「ぺ」とは違い、「ほ」+「゜」になっていることがポイントですね。そのコードポイントは\U309aなので、echo -eで「ほ」や「へ」にくっつけてからsedに渡し、置換。先の画像が有ったのですぐ気づいたのですが、本当はxxdなんかを見た方がいいですね。

PowerShellの解答は以下のかんじで

PowerShell

$ (cat pepo -Encoding UTF8) -replace "ぽ","  " -replace "ぺ","  " -replace "ほ\u309a","(" -replace "へ\u309a",")"
                  人
              (      )
            (          )
          (              )
        (                  )
      (                      )
    (                          )
  (                              )

sedを重ねるよりも簡単に欠ける感じがして結構好きです。方針は先のものと同じですね。

Q2

暗号化されたC言語のソースを解読してプログラムを復元する問題。ヒントが無かったら解けなかったです。

bash/zsh

$ echo -e "$(cat q2 | sed 'y/1234567890bcdefg/0123456789abcdef/' | sed 's/../\\x&/g' )" | uconv -x FullWidth-HalfWidth
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char a[] = {0x75,0x6e,0x6b,0x6f,0x00};
    puts(a);
    exit(0);
}

シーザー暗号ですというヒントがあったので、それを頼りに。数値は一つシフト、アルファベットはa-fb-gになっているので、それぞれ置換。全角文字になっているので、uconvを使って半角へ戻します。nkf -Zでもいいんですが、全角スペースが半角にならないらしいです。なんでなんだろう。

ところでこれを実行すると

$ ... | gcc -x c - && ./a.out
unko

うんこになります。PowerShellでもやってみますが、置換の部分だけ。半角<=>全角はuconvでよくない?ということで・・・。まぁそんなこと言ったら全部さっきの解答でいいんですけどね。

PowerShell

$ $hash=@{b='a';c='b';d='c';e='d';f='e';g='a';'1'=0;'2'=1;'3'=2;'4'=3;'5'=4;'6'=5;'7'=6;'8'=7;'9'=8;'0'=9}; [regex]::Replace((cat .\q2), ($hash.Keys -join "|"), {$hash[$args.value]}) | xxd -ps -r

-replacesedy相当の者が有ればよかったんですがね~。

Q3

factorawkを使って127が素数なことを判定する問題。ただし数字を含めてはだめらしい。難読化だ。

bash/zsh

$ @ &>/dev/null; echo $? | factor | awk "NF==(($$/$$+$$/$$))"

PowerShell

$ echo (((([int]('a' -eq 'a')) -shl (([int]('' -eq '') -shl ([int]('b' -eq 'b')) -shl ([int]($null -eq $null)) -shl [int]$?))) -shr [int]$?) - [int]$?)

どちらも特に解説することは・・・ないですね。Trueな値をintへキャストすると、1になるということさえ分かっていれば、PowerShellの方はわかるかと思います。

Q4

アルファベットを使わずにq2ファイルの中身を出力する問題。zshだと<?2でできるんだけど。

bash/zsh

$ $0 ?q

bashに与えて、コマンドとして評価させます。エラーが出るけど、中身はチェックできるのでOKだよね。

Q5

messageファイルを解読する問題、なんだけど順を追って解読していくので、ここではサブ問題みたいな感じ。この後の問題とつながとのこと。ここでは暗号をデカい16進数として見て、素数で順番に割って行く問題となっています。

bash

$ seq 20 | factor | awk -v V=$(cat She*/vol.51/message) 'NF==2{printf("%s/%X\n",V,$2)}' | sed 1i'obase=16;ibase=16' | bc
1E507BF014FBCE45D6079643F0151B79E27113EF90AA1B15EA94D9D
1435A7F563528983E4050ED7F56367A696F60D4A60716763F1B8913
C2031933B97EC1BEF363C1B2CD53E30C0FA07F96D10D7A25DD523E
8A947FB73B5A8A63D26BD37FB73BEB51C204ED6BB9E50E1B0BCD08
582FF42EC8ADFAF9B473297FD1B1C44B4CEBDCE7764BF1B41EC84B
4A9EBAEC826BD45D2288D458765B57536878CE9C77CA1B49A3E48E
390FF85A81D9FCBFB0FF38F85A82159A22B6BC1D4C8B7E475F364E
330E4A00235740759E5D9EC34381DD6EF6A37FE451F612ABA6082B

16進数での式を組み立ててbcに計算させます。sed 1iがポイントですね。これヘッダーを付けたりするときによく使います。

PoweShell版はこちら

PowerShell

$ $V=([System.Numerics.BigInteger]::Parse((cat .\message), [System.Globalization.NumberStyles]::AllowHexSpecifier)); (1..100 | %{ openssl prime $_ } |sls "is prime" |%{ ($_ -split ' ')[1] }) -replace '[(|)]','' | %{ ($V/$_).ToString("X") }

まずはHexな文字列をBigIntegerとして読ませるために[System.Numerics.BigInteger]::Parseを使います。その後は openssl prime素数判定、切り出し、計算の順番に処理をしていきます。($V/$_).ToString("X")BigIntegerHexな文字列として出力させる部分ですね。

Q6

上のあとに続けてメッセージを探す問題。雑にxxdへ渡すだけなんだけど、検索が上手くできなくて、時間内に解けませんでした。にゃーん

bash/zsh

$ seq inf | factor | awk -v V=$(cat message) 'NF==2{printf("%s/%X\n",V,$2)}' | sed 1i'obase=16;ibase=16' | bc | xxd -ps -r | grep -m1 -aoP '[\p{Han}\p{Hiragana}!]{3,}'
目だ!目を狙え!

解説でもあったけど、grep-a\p{Han}とか\p{Hiragana}を良く忘れてしまう。好きなんだけどな~~。

PowerShellのほうは多分こんな感じ

PowerShell

$ ... |xxd -ps -r |sls "[\p{IsHiragana}\p{IsCJKUnifiedIdeographs}!]{3,}"

.NETでは\p{IsHan}がないっぽいのでこれで仕方なく代用してますが、CJKなので無限に引っかかる。いつか見つかると思います・・・

Q7

ssh localhostした後、記号だけでbashを立ち上げる問題。echo $SHLVLの出力が2になるような記号ワンライナー

bash/zsh

$ ${!##-}

${!#}は関節参照展開。${#}0なので、${!#}${0}と評価される。これでシェルが呼び出せそうなんだけど、$0-bashとか-zshになることがあるので、${var#-}で先頭のハイフンを削っているということ。解説通りなので、配信のアーカイブを見た方がいいかもです。

Q8

echo evilという文字列をランダムな文字列が書かれたファイルの中に隠す問題。隠し方は以下

  1. 元の文章を2進数にする
  2. 各ビットをランダム文字列の各文字の最下位ビットとして仕込む

bash/zsh

$ echo echo evil | xxd -b -c1 | sel 2 | grep -o . | while read L; do [[ "$L" == "1" ]] && shuf -n1 -e {A..Z..2} || shuf -n1 -e {B..Z..2}; done
ZMSXHSFYJCAFJJOYNCIZWJNLTKMHGAISJJYTFZVFLAMZREZYVKGWZQKDNKOBMXFWJKORKAJNPHZDCBOP

最初はAかBしか出力しないやつだったんだけど、ランダム化しました。ちなみに{A..Z..2}という書き方、zshでは出来ませんでした。

出来てほしいな~という感じですね。

さて復元もしてみます。

bash/zsh

$ echo -n ZMSXHSFYJCAFJJOYNCIZWJNLTKMHGAISJJYTFZVFLAMZREZYVKGWZQKDNKOBMXFWJKORKAJNPHZDCBOP | xxd -ps -u | sed 's@..@&%2\n@g' | sed 1i'obase=16;ibase=16;' | bc | tr -d \n | fold -b8 | sed 1i'obase=16;ibase=2'| awk 4 | bc | xxd -ps -r 
echo evil

基数変換が面倒すぎません・・・?なんかエイリアス貼るなどしたいですね。

PowerShell版もサクッと

PowerShell

$ ((echo "echo evil" | xxd -b | %{($_ -split ' ')[1..6]}) -split '' |? {$_} | %{if($_%2){"ACEGIKMOQSUWY"[((Get-Random)%13)]}else{"BDFHJLNPRTVXZ"[((Get-Random)%13)]}}) -join ""
HYIPJELUHUSXZFKKHGCTQFFJVIWTEMMMLFOHDTHJHWKHTWNMDAEGLKMRLWSPOBPQLWAFEAFNLLPHEKDUXPNNMTQN

$ $A=((((echo HYIPJELUHUSXZFKKHGCTQFFJVIWTEMMMLFOHDTHJHWKHTWNMDAEGLKMRLWSPOBPQLWAFEAFNLLPHEKDUXPNNMTQN | xxd -ps -u -c1 | %{ [int]::Parse($_, [System.Globalization.NumberStyles]::AllowHexSpecifier)%2}) -join "") -replace "(.{8})",'$1 ') -split " " | %{[System.Convert]::ToInt32($_, 2).ToString("X2")});$A[0..($A.Length-2)] | xxd -ps -r

サクッとと言いましたがすごくggったりしました。むずい

LT

今回もLTさせていただきました。

www2.slideshare.net

t.co

12月なのでワタナベエンコーディングについて再考し、再実装しました。とてもいいものが出来たと思います。聴いてくださった方ありがとうございました!!

おわり

今回は基数などの変換が多かったイメージです。難読化系の問題は得意なんですが、制限時間があると、どうしてもよくある解答以外私から出てこなくなってしまうのが痛いですね。難読火力を磨かなければですね。

とても楽しかったです!企画・開催ありがとうございました!!

雑記 2020-11-13

焼きそば

昼飯はよく焼きそばを作る。予め切っておいた野菜やら肉やらをエイと炒めるだけ。洗い物が少々面倒だが楽なので良い。 買い物に行くときは冷蔵庫を覗いて材料が残っているかをチェック、次の買い物までに足りなくなりそうなら買い足しておく。なんか得意げに書いているが当然のことだ。

ところが「足りなくなりそうなら」と言う部分のロジックがバグっていたのか、今はなんと麺が6袋ある。つまり6日分。近々冷蔵庫を空にしなければならない予定があるので、こいつをどうにか消費しきらなければならない。とりあえずイレギュラーだが、今日の晩ごはんに2袋使おうと思う。さて…僕は食いきれるだろうか…

man

[[ について調べるとき、あまりにもggりにくくてmanに駆け込むことになった。なんかmanってあんまり得意じゃなかったんだけど、いつの間にか普通に使えたので僕も進化しているんだなあと思った。

おわり

おわりです

雑記 2020-11-10

チョコ

あんまり間食はしない…というよりはコーヒーをゆっくりゆっくり啜っているので、他になにか食うタイミングがない。にも関わらずおまけ欲しさにコンビニでチョコをたくさん買ってしまった。
皆いつおやつタイムを取るんだろう。思い返せば僕がもっと小さかった頃はバリバリおやつを食っていた気がする。あれは何時ぐらいだったんだ?

ワイヤレスイヤホン

料理をするとき、動画を見ながらよくやる。換気扇とかフライパンとか他にも音のなるものが多いので、動画の音声をよく聞くためにコード付きのイヤホンをしている。ところがこれ、かなり邪魔。というか危ない。
それを解決したくて、少し前ワイヤレスイヤホンを買った。店頭で結構迷って選んだもの。最初に決めた予算は超えたが…まぁオタクの買い物ってだいたいこうなるしいいか、と思った。
ところがいざ試してみると、電子レンジで詰んだ。そりゃあそうだよなあと思った。そう、それはそうなんだよな。

おわり

はいはいおわりおわり