たいちょーの雑記

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

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

しました

午前

構造化プログラミング入門の2回目でした。ジャクソン流構造化プログラミングについての講義で、例題に図を描いてみたりなどをしました。

配信はこちら

午後

今回はPowerShellでも解きます。問題と解答はこちら

Q1

1行目に今日の日付、3行目以降にN日後の予定が書かれているファイルがあります。このN日後を具体的な日付になおしてくださいという問題。

$ cat She*/vol.58/memo | sed 's/日後/ days@/' | awk 'NR==1{d=$2}NR>2{$1=d;print}' | sed 'y/年月日/-- /' | teip -f1 -d@ -- zsh -c "while read L;do date -d \$L +'* %Y年%m月%d日';done" | tr -d @
* 20220307日: ワクチン接種
* 20220311日: 国際ロボット展
* 20220312日: シェル芸勉強会

N日後の部分を、2022-03-05 n daysに変換、teipで日付のフィールドだけ選択して date に渡し、日付を計算させる感じです。

PowerShellでもやってみます。

$ DATE=iex 'cat .\memo |%{"$_" -split " "} | select -Index 1'
$ cat .\memo | select -Skip 2 | %{ $x="$_" -split " "; $x[1]=(date $DATE).AddDays(+($x[1] -replace "日後:","")).ToString("yyyy年MM月dd日:");$x -join " " }

Get-Dateで日付のオブジェクトが得られ、そこに生えてるAddDaysで日付を計算して出力しています。 .NETを少し触るので System.Hogeに生えてる!みたいなのはちょっとわかるんですけどPowerShell独特の書き方がまだわからんですね。

Q2

数字を使わずに date コマンドに 2022年02月22日 2 22:22:22 を出力させる問題。日付と時間の間にあるのは、火曜日を表す2とのこと

false;a=$?;true;b=$?;date --date "$(date +%Y)-$b$((a+a))-$((a+a))$((a+a)) $((a+a))$((a+a)):$((a+a))$((a+a)):$((a+a))$((a+a))" +"%Y年%m月%d日 %u %T"

無理やり。数値を終了コードから持ってきて計算を繰り返すという感じです。

$ Get-Date (Get-Date).AddMonths(-1).AddDays(7) -UFormat "%Y年%m月%d日 %u %d:%d:%d"
2022年02月22日 2 22:22:22

PowerShellの方ではtwitterに投稿された賢い方法でやりました。2022年3月15日に書いてるので、足したり引いたりする部分は当日とは違いますが

Q3

jsonyamlに変換する問題

$ cat 13.json | gron | grep line_ | sel -gd "\.|=" -D: 3 4 | sed 's/ :/:/' | tr -d \; | sed 's/line_cd/  - &/;s/line_name/    &/;1iline:' | head

yqを使えば一撃なんですが、それなしで解いてみました。必要なところ切り出して~インデントつけて~って少々面倒になっちゃいますね。やはり決まったデータ構造の変換は専用のツールに任せるのが楽ですね

$ cat .\13.json | ConvertFrom-Json | ConvertTo-Yaml

PowerShellにはpowershell-yamlというモジュールがあり、変換をえいでやってくれるので楽です。

Q4

尺取り虫みたいな動きをするうんこの動画を作る問題。しゃくとりうんこというらしい…うむ…

$ yes $(echo 💩{,,,,,,,,,}| tr -d ' ') | awk -F '' '{$(NR%10?NR%10:10)="@";print}' OFS='' | rev | awk '{for(i=0;i<NR/10;i++)printf " ";print}' | tr @ ' ' | head -n 300 | textimg -a

このワンライナーのうち、textimgの部分を外すと

 💩💩💩💩💩💩💩💩💩
 💩💩💩💩💩💩💩💩 💩
 💩💩💩💩💩💩💩 💩💩
 💩💩💩💩💩💩 💩💩💩
 💩💩💩💩💩 💩💩💩💩
 💩💩💩💩 💩💩💩💩💩
 💩💩💩 💩💩💩💩💩💩
 💩💩 💩💩💩💩💩💩💩
 💩 💩💩💩💩💩💩💩💩
  💩💩💩💩💩💩💩💩💩
  💩💩💩💩💩💩💩💩💩
  💩💩💩💩💩💩💩💩 💩
  💩💩💩💩💩💩💩 💩💩
  💩💩💩💩💩💩 💩💩💩
  💩💩💩💩💩 💩💩💩💩
  💩💩💩💩 💩💩💩💩💩
  💩💩💩 💩💩💩💩💩💩
  💩💩 💩💩💩💩💩💩💩
  💩 💩💩💩💩💩💩💩💩
   💩💩💩💩💩💩💩💩💩
   💩💩💩💩💩💩💩💩💩
   💩💩💩💩💩💩💩💩 💩
   💩💩💩💩💩💩💩 💩💩
   💩💩💩💩💩💩 💩💩💩
   💩💩💩💩💩 💩💩💩💩
   💩💩💩💩 💩💩💩💩💩
   💩💩💩 💩💩💩💩💩💩
   💩💩 💩💩💩💩💩💩💩
   💩 💩💩💩💩💩💩💩💩
    💩💩💩💩💩💩💩💩💩
    💩💩💩💩💩💩💩💩💩
    💩💩💩💩💩💩💩💩 💩
    💩💩💩💩💩💩💩 💩💩
...

10行ごとにインデントが深くなるしゃくとりうんこが出力されます。2つのawkでできていて、前半はしゃくとり動作、後半はインデントを頭につけるものですね

$ $NR=0;[System.Linq.Enumerable]::Repeat([System.Linq.Enumerable]::Repeat('💩', 10) -join " ",30) |%{$x="$_" -split " ";$x[$NR%10]=" ";[array]::Reverse($x);" "*[Math]::Floor(($NR/10))+($x -join "");$NR++}

PowerShellでも同様の指針です。$NR/10が四捨五入されるというのにハマってしまったのでPowerShellむずい…ってなりました

Q5

危険シェル芸なのでスキップ

Q6

file1file2から似た文字列のペアを作る問題。

join -j9 She*/vol.58/file* | while read L R; do echo $L $R $(echo $L | grep -o . | sort | diff - <(echo $R | grep -o . | sort) | wc -l); done | sort -nk3 | head -n4 | sel 1 2

方針としてはなんちゃって編集距離を計算して、距離が近いもの同士をペアにするというものです。単語を分解してソート、diffを取って出力行数を編集距離とし、出力します。これをすべてのペアで行い、小さい順に取り出すという感じです。

$ cat .\file1 | Join-Object -RightObject (cat .\file2) -JoinType Cross |%{ @{x=$_;c=(diff ($_[0] -split "" | sort) ($_[1] -split "" | sort)).Count} } | Sort-Object -Property c | select -First 4 |%{$_.x -join " "}

PowerShellでも同じ方針にしてみました。Join-ObjectはJoinModuleで入手できます。

Q7

めちゃデカファイル nums.gz には数値がたくさん書いてあるので、その合計を計算する問題。

$ (zgrep '[0-9]' nums.gz ;zgrep -v '[0-9]' nums.gz | numconv) | awk '{s+=$1}END{print s}'

時間中に解けなかったので解説通りの解で。zgrepに与える正規表現^[0-9]*$にしたら一生終わらなかったので最小にしてみました。処理時間が話題になっていたので自分でも測ってみると

11.78s user 1.30s system 80% cpu 16.251 total

でした。PowerShellのほうはzcat相当のことをやるのがめんどくさかったのでやめときました

LT

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

www.slideshare.net

今回はsurgeというコマンドを作りましたという話をしました。「行を列に変換し、任意のコマンドのSTDINへ流し込み、その出力を行に戻して出力する」というものです。while read L; do ... doneのショートハンド的なコマンドですね。while read L;do ... doneだとパフォーマンスが残念なこと、コマンドを適用したいだけなのにそれ以外の部分の分量が多くてつらいことが気になっていたので作ってみました。

そのうち個別の記事でも書きたいと思っています。

ちなみに surge を使って以下のように書けば

$ (zgrep '[0-9]' nums.gz ;zgrep -v '[0-9]' nums.gz | numconv) |  paste -{,,,,,} | surge -P 20 -- awk '{s+=$1}END{print s}' | awk '{s+=$1}END{print s}'

Q7を並列で計算させることもできるんですが、むしろ遅くなるので適材適所って感じがしますね。

おわり

今回もとても楽しかったです!次回は自分の気になる話があるのとことだったので今から楽しみです。企画・開催ありがとうございました!