たいちょーの雑記

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

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

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

今回も午後のみ。余裕があればPowerShell解も提示します。出題された問題などは配信アーカイブのこの辺から

Q1

inoutファイルにA,B,Cさんの入退出記録が書いてあります。入退出が矛盾する人がいるので、その人を見つけてくださいという問題。

A 入 
C 入
B 入
B 出
A 出
C 入
B 入
B 出
C 出
$ cat Sh*/vol.61/inout | awk '{a[$1]=a[$1]""$2}END{for(i in a)print i, a[i]}' | grep -Pe "(.)\1"

1列目をキーに連想配列に入退出記録を詰めていき、それを出力します。そうすると以下みたいになります

A 入出
B 入出入出
C 入入出

出出とか入入みたいになっているのは矛盾している部分なのでそれを見つけるようなgrepを書いてひっかけるって感じですね。これだと から始まる矛盾に対応できないというのをTwitterで見かけて確かにと思いました。その場合は grep -Pe "(.)\1|^\d+ 出"みたいにすればできそうですね。

PowerShell解も示します。方針は同じ感じです

$ cat .\inout | %{@{K=$_[0];V=$_[2]}} | group -Property K | %{$_.Name + " " + (($_.Group|%{$_.V}) -join "")} | sls '(.)\1'

Group-Objectを使うために一回Hashtableにしたんですがこれやらない方法ないんですかね?

Q2

dataファイルから3行連続で文字列が記述されている部分を抜き出す問題。ファイルの内容はともかく、割と普段もやる感じな問題ですね。

$ cat Sh*/vol.61/data | tr \\n @ | sed -E 's/@@+/\n/g;s/@$//g' | grep -Pe '^[^@]*@[^@]*@[^@]*$' | tr @ \\n
$ cat Sh*/vol.61/data | awk '!NF{print a;a=0}NF{a++;print}END{print a}' | grep 3 -B3 | tr -d 3-

一つ目のほうは改行を@に置き換え。そのあと特定の個数だけ@が現れるようなものだけ取り出して改行を復元しています。二つ目の方は連続が途切れたタイミングで連続していた量を出力しgrep-Bでひっかけるというものですね。

二つ目の方いいな~と思ったので、同じ方針でPowerShellでもやってみます。

$ cat .\data | %{if($_ -eq ""){echo $x;$x=0}else{$x++;echo $_}} -Begin {$x=0} -End {if($x -eq 3){echo $x}} | sls -Context 3,0 3

Select-String-Contextで前後行を出力できます。a,bみたいにすると前a行、後ろb行になります。しらんかった

Q3

1~100までの数値が区切り無しで一行に書かれたnumsというファイルがあります。これに適切にスペースをいれてくださいという問題。難しそう

$ cat Sh*/vol.61/nums | sed -E 's/[1-9]0?0/\n&/g' | sed '1s/./& /g;2,10s/../& /g' | xargs

1つめのsedまでで以下のようになります

123456789
10111213141516171819
20212223242526272829
30313233343536373839
40414243444546474849
50515253545556575859
60616263646566676869
70717273747576777879
80818283848586878889
90919293949596979899
100

いい感じに別れましたね。あとは2つめのsedでスペースを挿入していきます。a,bを付けて適用する行を選んでいます。

PowerShellでもやり…ますか…。

$ ((cat .\nums) -replace "([1-9]0?0)","`n`$1" -split "`n" | % -Begin{$NR=0} {$NR=$NR+1;if($NR -eq 1){$_ -replace "(.)","`$1 "}elseif($NR -ge 2 -and $NR -le 10){$_ -replace "(..)","`$1 "}else{$_}}) -join ""

愚直~。

Q4

file1の最後の改行を削除する問題。この問題の逆はたまにやりますね

$ cat Sh*/vol.61/file1 | xxd -p | sed 's/..$//' | xxd -p -r
$ cut -zb -"$(($(wc -c She*/vol.61/file1|sel 1)-1))"  She*/vol.61/file1

1つめはxxdでバイト列にし後ろの1バイトを削除。後ろの1バイトは改行なのでバイト列から元の文字列に戻すと改行が消えるという感じですね。二つ目はcutコマンドで先頭からNバイト取り出すという感じ。Nバイトを動的に計算するためにwcを使っています

PowerShellでもやってみます。

$ $a=(cat -AsByteStream ./file1);[System.Text.Encoding]::UTF8.GetString($a[1..($a.Length-2)])

後ろの1バイトを削る方針です。.NETのクラスとかが使えるので魔術感が減って読みやすい感じが…しますね?

Q5

nums2というファイルは以下のような内容になっています。

12423285230975943

これを以下のように成形するという問題。

1   3  5 3 9759 3
 242 28 2 0    4
$ cat Sh*/vol.61/nums2 | sel -D@ 1 1 | teip -d@ -f2 -- sed -E 's/[13579]/ /g' | teip -d@ -f1 -- sed 's/[24680]/ /g' | tr @ \\n

teipを使いたかった…。1行1列を1行2列にし、それぞれ奇数をスペースに、偶数をスペースにするsedを書いて改行して終了。という感じですね。でもこれ2行1列にしてsedのアドレスでいいですね…。

PowerShellだと以下のような感じです。

$ cat .\nums2 | %{$_ -replace "[24680]"," "; $_ -replace "[13579]"," "}

複製しなくても$_に対して2回置換をやるだけでいいってわけですね。

ところでこの問題。数値を使わずにというオプションもあったのですが、それをPowerShellでやろうとすると

$ cat ".\nums$($true+$true)" | %{$_ -replace "[$($true+$true)$($true+$true+$true+$true)$($true+$true+$true+$true+$true+$true)$($true+$true+$true+$true+$true+$true+$true+$true)]"," "; $_ -replace "[$(+$true)$($true+$true+$true)$($true+$true+$true+$true+$true)$($true+$true+$true+$true+$true+$true+$true)$($true+$true+$true+$true+$true+$true+$true+$true+$true)]"," "}

ウワー!!!

Q6

nums3のうち現在の行からみて3つ先が3倍の数な行を取り出す問題。ムズ

$ cat Sh*/vol.61/nums3 | fmt -1 | paste - <(fmt -1 Sh*/vol.61/nums3|tail +4) | awk '$1*3==$2{print $1}'
# awkなし
$ cat Sh*/vol.61/nums3 | fmt -1 | sel 1 1 1 | surge -- jq -s add | paste <(fmt -1 Sh*/vol.61/nums3) <(fmt -1 Sh*/vol.61/nums3|tail +4) - | grep -Pe '(\d+)\s(\d+)\s\2$' | cut -f1

pasteを使ってnums3nums3をくっつけます。このときtailで3行先が横に来るようにしておきます。あとはawkで左右を比較すればいいですね。

そしてこの問題、awkを使わずに…というものがありました。それが2つめの解答なんですが…。面倒なので割愛です。

PowerShellでは以下のようにしてみました

$ $a=((cat .\nums3) -split ' ');[System.Linq.Enumerable]::Zip($a, ($a[3..($a.Length-1)]), [Func[Object,Object,Object[]]]{ param($x,$y) @{x=$x;y=([int]$x*3 -eq $y)}}) |?{$_.y}|%{$_.x}

Zip思ったよりつらいですね

Q7

再帰的に計算を行い表を完成させる問題。

$ a() {awk '{s+=$1*$NF}END{print s}' Sh*/vol.61/prior} ; b() {awk -v X=$1 -v Y=$2 '{print $0,(Y=="表"?$1:(1-$1))*$NF/X}' Sh*/vol.61/prior}; cat Sh*/vol.61/coin | grep -o . | while read c; do  b "$(a)" "$c" | sponge She*/vol.61/prior; done;

正規か前の次の$NFの合計をするa()と、その合計をもらって正規化しながら$NFを追加していくb()を定義していい感じに呼び出します。わざわざ関数にしたんですがしなくてよかったやつですねこれね。再帰的に行いたいので結果はspongeで元のファイルに書き込んでいます。

PowerShell版は以下のような感じ。めちゃめちゃや

$ cat coin | %{$c=$_; $content=($content | %{$x=($_ -split " ");$y=+$x[0];if($c -eq "裏"){$y=1-$x[0]} "$_ $($y*+$x[1]/$s)"} -Begin{$content=(cat .\prior); $s=($content | %{$x=($_ -split " ");+$x[0]*+$x[1]} | measure -Sum).Sum})}; $content

LT

今回もLTさせていただきました!前回の続き物という感じです。

読み取り間違いをどうにか抑えていこう!って感じの取り組みでした。でも本番一発勝負では失敗しました。魔術師になるのは難しい。

おわり

今回はデータ整形系の問題が多くて試行錯誤とか変換過程をみるのが楽しかったです!企画運営の皆様ありがとうございました!