たいちょーの雑記

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

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

今回、大阪はサテライトが開催ということで現地参戦しました。

下みたいな感じで盛り上がってました

twitter.com

問題と解答

いつものように時間中に解いた答えなどを記録していきます。PowerShell解は気が向いたらやります

Q1

message.pptxからデータを削り出していき、unkoを得る問題

$ cat She*/*62/mess* | grep -aoPe 'u|n|k|o' | uniq | tr -d \\n | grep -o unko
unko

grep-aでバイナリ内を検索します。出てきた文字から連続部分をuniqで圧縮したり、trで改行を消したあと、最後にgrepunkoを取り出すという感じですね。削り出しでいいのでこれで解答としました

Q2

imgディレクトリ以下のファイルがPNGかどうかを判断する問題

$ ls She*/*.62/img/* | awk '{print "xxd", $1, "|head -n1|grep -q PNG && echo",$1,"PNG || echo",$1,"PNGじゃない"}' | bash
ShellGeiData/vol.62/img/img1.png PNG
ShellGeiData/vol.62/img/img2.png PNGじゃない
ShellGeiData/vol.62/img/img3.png PNG
ShellGeiData/vol.62/img/img4.png PNGじゃない

バイナリを直接見る感じでやってみました。でもこれねPNGって書いてあったらだめだねこれね。fileで判断するのが賢いと思います

Q3

PNG画像からプログラム取り出して実行する問題

$ convert She*/*.62/prog* pgm:- | sed 1,3d > a && chmod +x ./a && ./a 
これはUNKOの画像です。

PGMから生成されたというヒントがあったので、PGMに戻してやったあとのヘッダー部分を取り除いた残りの部分をファイルに書き出しました。実はこの部分がプログラムなので実行権限を付けてやれば解けるということですね。

Q4

さっきのような画像を作る問題

$ cat > src.pgm
P5
2 23
255
#!/bin/bash

echo おはようございます

$ convert src.pgm dest.PNG

$ convert dest.PNG pgm:- | sed 1,3d | bash
おはようございます

解答例としても紹介されていたシェルスクリプトを仕込む感じにしてみました。本当はowariとか仕込んでやればよかったんですけど、ファイルサイズがいい感じの素因数分解できなくて諦めました

Q5

message.pptxに書いてあるメッセージを読み取る問題

$ cd She*/*.62/
$ unzip messag* &> /dev/null
$ cat ppt/slides/slide1.xml | grep -oPe '<a:t>[^<]*</a:t>' | sel -gd'<|>' 3
メッセージ
これを読んでいるころは、
シェル芸勉強会に参加されていることでしょう。
どうせ、yes ウンコとかコマンドを
打たさせていると思うと、大変残念です。
どんまいける!

どんまいける!pptxはzipっていうのはいつもそういえばそうでしたって感じになりますね。

Q6

nanika_data.tar内にあるファイルをタイムスタンプ順で並び替え ファイル名 -> 内容 の順番で読みだすと文章になります。ここからいろいろ取り出してくださいという問題

時間内には解けず…。tarの仕様を読んだりTLの解答を試したりしてました。\0が厄介でしたね~~~

LT

今回もLTしました!!!!!!!

github.com

ワタナベにとりつかれてしまったという話をしてしまいました。

takaさんのLTがかなり興味深かったです。勉強になる

おわり

今回も楽しかったです。近くに呻いている人がいるのはいいですね。 企画運営などありがとうございました!

第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させていただきました!前回の続き物という感じです。

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

おわり

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

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

参加しました

問題

Q1

echoコマンドを使わずに A=$(echo {a,b,c,d}{1,2,3,4})相当のことを実行する問題。これ進研ゼミ(難読化シェル芸)でみたやつだ!

set {a,b,c,d}{1,2,3,4}
A=$@

echo $A

setを使うと位置パラメータに展開されるので、$Aに再代入すれば良いのです。zshだと (){$A=$@} {a,b,c,d}{1,2,3,4}; echo $Aという感じに無名関数を呼べるので、引数をAに展開するようにしてえいです

Q2

秒数が3の倍数の時にアホとターミナルに出力する関数をできるだけ短く書く問題

f(){[[ "$(date +%S|awk '($1%3)==0')" != "" ]] && echo アホ; sleep 1; f}

date+%Sで現在の秒数だけ出力します。シェルの$(())で剰余を求めてもいいんですけど、09のときに8進数として壊れてしまうのでawkで計算しています。関数の最後で自分自身を呼び出して再帰しています。

Q3

/を使わず ls / / / / / / / / / /相当のコマンドを実行する問題。難読化か

: $(pwd);yes "ls ${_::1}" | head -n10 | bash

: $(pwd)$_に現在のパス展開します。$_から/を切り出してコマンドを作り出します。yesheadで10個にしてbashに渡します。

Q4

dangerファイルの中から実行してしまうとファイルを削除してしまうコマンドを探す問題。

 cat S*/*60/da* | sed 's@^@echo @' | bash | grep -nv '*'

先頭にechoをつければクォートがおかしいコマンドについては展開されます。これを利用して*がなくなってしまった行だけ取り出して終了です。これ、難読化シェル芸はechoするとすべてわかるっていうのに似ていますね。

Q5

ls -lの出力のスペースをつめる問題。ただし、ls以外の外部コマンドを使わないこと。という問題

set "$(/bin/ls -l)"; echo "${@//  / }"

短くかけたんですが、スペースが3個以上になることがあり敗北です。

Q6

figletの出力2つを受け取り、それらを横に連結する関数を作る問題。小問1は引数2つまで、小問2は引数が何個でもできるものを作ります。

なんだけどいきなり小問2から始めてしまったのでこれで

f(){ X=$(paste <(echo "$1") <(echo "$2")); shift; shift && f "$X" "$@" || echo $X }

引数をshiftで切り出しながらpasteで連結。引数がまだ残っていれば再帰してやります。なければ出力して終了です。

Q7

タイムアップ

Q8

以下のような出力が得られる変数Aを作る問題

$ echo $A
c

$ echo $A | grep a
c

なんだこれは…。

A="\033[ac"
echo $A;
echo $A | grep a

エスケープシーケンス内にaを仕込んでしまう方法を試してみました。手元のzshだとうまくいったので投稿してみたんですが、botだといい感じにならなかったですね。難しい。

LT

www.slideshare.net

今回もLTさせていただきました!yukichantの出力を画像にし、高速に詠唱する試みです。聞いてくださった皆様ありがとうございました!

おわり

今回は久しぶりの物理会場ということで、苦しんでる人が近くにいるとなんだか安心するなあという感覚がよみがえりました。次回はどうなることかわかりませんが、また参加出来たらと思います。企画運営の皆様ありがとうございました。

owariコマンドを実行するActionを書きました

書きました。常々CIと相性がいいんじゃないか?と思っていたんです。ということで、Actionの書き方の勉強ついでに作ってみました。

github.com

owariとは

github.com

owariは終焉にまつわるアスキーアートを出力するだけのジョークコマンドです。このブログでも何度か書いていますね。

xztaityozx.hatenablog.com xztaityozx.hatenablog.com

何かが終わったときに表示されそうなAAを出力するので、処理の長いコマンドの後に && owari とつけ足しておくと、なんだか楽しい気分になります。

その他には記事の最後とか、スライドの最後とかに使われていたりしますね。

CI/CDでもowari

CIというのはそれなりに時間のかかるものです。素早く処理させるために、キャッシュしたり、ワークフローを見直したりなど皆さん頑張っていることと思います。 owari-actionはそのお手伝いができます。

いえ、実行時間を短縮するような仕組みは一切持っていません(むしろ伸びます)。ただ、なんだか楽しい気分にしてくれます。なんだか楽しい気分にしてくれるので、実行時間が長くてもまぁいいか!となるわけですね。

使い方

いつものようにyamlに書くだけです。

name: example

on: [push]

jobs:
  example:
    runs-on: ubuntu-latest
    steps:
      - name: default
        uses: xztaityozx/owari-action@v1.0.0

      - name: owari kanban
        uses: xztaityozx/owari-action@v1.0.0
        with:
          args: 'kanban -a xztaityozx owari-action'

      - name: as job summary
        uses: xztaityozx/owari-action@v1.0.0
        with:
          as-summary: true

      - uses: xztaityozx/owari-action@v1.0.0
        with:
          args: 'kanban -a xztaityozx "これは スペースが 含まれている 文字列"'

実行結果はこんな感じです。

inputs.as-summary

trueにしておくと、workflowの詳細ページに以下のような感じのサマリーを吐き出してくれます。デフォルト値は false です

owari-as-summary

inputs.args

owariコマンドへの引数です。デフォルト値は default -a ${{github.repository}} です

outputs.content

owariコマンドの出力を outputs.content に格納しているので、owari-actionの後のstepとかでも使えます

jobs:
  example:
    runs-on: ubuntu-latest
    steps:
      - name: default
        id: owari
        uses: xztaityozx/owari-action@v1.0.0

      - run: echo ${{steps.owari.outputs.content}}

仕組み

Dockerコンテナのアクションを作るチュートリアルに従って作りました。owariのバイナリをダウンロードして引数をよしなに渡すってだけです。

簡単に作れたので良かったんですが、DockerコンテナのアクションはLinuxオペレーティングシステムじゃないとダメということなので、JSでやればよかったなって感じです。気が向いたらなおします。あと、やっぱりフォント問題でずれてるので難しいねって思います。

おわり

なんだか楽しい気分になるだけですが、よければ使ってみてください。


          糸冬
-------------------------
  制作・著作 xztaityozx

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

参加しました

午前

構造化プログラミング入門の第三回。涙の最終回です。

テーブル駆動方式とは論理分の代わりにテーブルの情報を使ってプログラムを制御するもの。 状態遷移表を素直にプログラムにする感じ。講義の途中で演習がありました。自分はC#で解答したので以下に貼っておきます。

int[,] nextState = new int[8,6]{
  {-1, 1,3, 2,-1,-1},
  {-1,-1,3, 2,-1,-1},
  {-1,-1,4, 2,-1,-1},
  {-1,-1,3, 4, 5,-2},
  {-1,-1,4,-1, 5,-2},
  {-1, 6,7,-1,-1,-1},
  {-1,-1,7,-1,-1,-1},
  {-1,-1,7,-1,-1,-2},
};

int getCharType(byte b) {
  if(b == '+' || b == '-') return 1;
  if('0' <= b && b <= '9') return 2;
  if(b == '.') return 3;
  if(b == 'E' || b == 'e') return 4;
  return 0;
}

bool IsFloat(string s) {
  var state = 0;
  foreach(var b in System.Text.Encoding.UTF8.GetBytes(s)) {
    var type = getCharType(b);
    state = nextState[state, type];
    if(state < 0) break;
  }
  if(state == -1) return false;
  if(state == -2) return true;
  return nextState[state,5] == -2;
}

var tests = new [] {
  ("", false),
    ("+", false),
    ("-", false),
    (".", false),
    ("+.", false),
    ("-.", false),
    ("+1+", false),
    ("-1-", false),
    ("+1", true),
    ("-1", true),
    ("1", true),
    ("+1.", true),
    ("-1.", true),
    ("1.", true),
    ("+.1", true),
    ("-.1", true),
    (".1", true),
    ("+1.1", true),
    ("-1.1", true),
    ("1.1", true),
    ("+1.1E", false),
    ("+1.1E+", false),
    ("+1.1E-", false),
    ("+1.1E.", false),
    ("+1.1E1", true),
    ("+1.1E+1", true),
    ("+1.1E-1", true),
    ("+1.1E.1", false),
    ("+1.1E1.", false),
    ("+1.1e+1", true),
    ("+1234567890.1234567890E+1234567890", true),
};

foreach (var t in tests) {
  Console.WriteLine("'{0}'\t{1}",t.Item1, t.Item2 == IsFloat(t.Item1) ? "OK" : "NG");
}

演習2はnextStateint[8,256]に書き換える問題でした。ここまでするとコードから状態遷移表を読み取ることができなくなる。つまり使いすぎ注意ということ。

午後

問題1

1億カラムあるファイルの 87654321 カラム目を出力する問題

$ awk '{print $87654321}' yoko

これだと遅いので後ろから読むなどすれば速いと思うので sel の後ろから読むモードで…と思ったけどラインバッファを超えて読み込めなかったです。笑っちゃうね…。

問題2

nums.gzに逆順の行番号をつける問題。

$ seq 1e8 | tac | paste - <(zcat nums.gz)

seq 1e8 -1 1も試したけどこれは遅い。stepが1の時は最適化されてるからというのをtwitterでみてなるほどなあと思いました。

問題3

nums.gzから漢数字を覗いて数値順にソートする問題。できるだけはやいと良いとのこと

zgrep '^[0-9]*$' nums.gz | split --lines=1000000; ls x*|xargs -n1 -P0 -I@ zsh -c 'sort @ | sponge @';  sort -nm x* > ans

splitを使ってファイルを分割。それぞれをいったんソートして sortマージソートするというもの。そこそこの時間で終わるのでいい感じだけど、ソートの結果あってるんかなこれ。

問題4

2020.tar.bz2を展開して出てきたファイルから

  1. 最もファイルサイズが大きいものを探す
  2. 展開せずに小問1と同じものを探す

という問題。

# 小問1
$ ls -rl --sort=none ./2020/ | awk 'max < $5{a=$0;max=$5}END{print a}'
# 小問2
$ tar -tvf ./2020.tar.bz2 | awk 'max < $5{a=$0;max=$5}END{print a}'

エントリ数がスゲーのでlsにソートさせると相当時間がかかってしまいます。というわけで--sort=noneでこれを無効に。あとはawkで最大値を探すだけです。小問2の方はtar -tvfの出力から同じことをするだけですね。

ところでいつもalias ls=exaしてるので素のlsを使おうってなったときちょっとハマります

問題5

2020.tar.bz2を展開して出てきた 2020 ディレクトリで

  1. hoge.cgiのうちhogeにあたる部分でディレクトリを作る
  2. 2020以下にあるファイルをそれぞれのディレクトリに移動させる(hoge.cgiならhogeディレクトリにmv)

という問題。

# 小問1
ls -U | sel -d. 1 | sort -u | sed 's:^:mkdir &:e'
# 小問2
fd . --type=f --max-depth=1 | rargs -j10 -p './([^\.]+).(.+)' mv {} ./{1}/

小問1はまぁはい。という感じの回答です。もしかしたら sortの前に uniqをはさんだ方が速いかもしれないです。 小問2はfdrargsで。並列化もできるしまぁそこそこの時間で終わるんじゃないかな…?

問題6

たくさんのログファイルからコマンドを数え上げる問題。LANG=Cみたいなひっかけもあるのでこれを除外しながらいい感じにしないといけないとのこと。

$ fd . --type=f | surge -P10 -- zsh -c 'read A;grep -e "^+ " $A|grep -oPe "^\++ [^ ]+"|grep -v =|sel 2|sort -u' | sort -u | fmt -1 | sort | uniq -c | sort -rn

fdですべてを列挙、surgeにファイルを渡して並列で検索!爆速!みたいなのを考えて描いたんですが愚直にgrepに全部渡す野に比べて激おそでした。まぁそうだよね。grepってよくできてるんだわ

LT

ぐれさんの teip コマンドアップデート

最強のteipコマンドに新しい機能が入ったよ。という内容のLT。めちゃめちゃ便利そうでした。

次郎さんのPEGについて

https://www.slideshare.net/jiro4989/peg-251688145

Parsing Expression Grammar(PEG)についての話でした。以前次郎さんがTwitterでつぶやいてるのを見て気になってはいた内容でした。聞けてうれしい。便利そうなので自分でも使ってみようと思います。

終わり

最終回ということでした。準備など本当にありがとうございました。演習問題があったおかげで理解もしやすかったです。テーブル駆動方式は個人的に良く書くコードにも使えそうなので試してみようと思います。

午後の部も毎回問題の整備ありがとうございます。今回はパフォーマンスをかなり意識する問題ということで、いつも通りの「うごけばええやろ」以外のアプローチをいろいろ考えられて良かったです。

第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を並列で計算させることもできるんですが、むしろ遅くなるので適材適所って感じがしますね。

おわり

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

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

参加しました

今回は午前もありました

午前

今回は久々に午前がありました。配信はこちら。構造化プログラミングシリーズの1回目。 ダイクストラ大先生の言ってることを理解するにはGotoだらけのプログラムを書くと良いとのことで、状態遷移図とそれを途中まで実装したプログラムを埋める実習などで説明されました。 詳しいことは配信のアーカイブを見ればよいので書きませんが…。

オートマトンは昔少しだけ勉強したんだけど、GoToで書かれていれば読みやすいか?と聞かれるとそうかしら…?と思ってしまった。あんまり慣れてないからかな…?今度試してみようと思います。

午後

今回もzshのみ。PowerShellで解いてもいいけど体力が0を超えて-100になるので…。

配信はこちら

Q1

reiwaファイルから 令和 を数え上げる問題。

$ cat Sh*/vol.57/reiwa | uconv -x NFD | grep -o 令和 | wc -l
20

実はコードポイント違いの令が存在するので、それに騙されないようにする問題。ここではuconvユニコード正規化して数え上げる感じにしました。

Q2

kanji2ファイルの旧字体新字体に変換する問題。

$ curl http://www.gaoshukai.com/lab/0039/N_O_character.txt > o
$ cat Sh*/vol.57/kanji2 | while read L; do echo $L | grep -o . | while read R; do grep $R ./o 2>/dev/null || echo $R; done | awk '{print $1}' | paste -sd ''; done
写真
星図
学校
鉄道
鉄砲弾
灯台
両腕

インターネットに転がっていた辞書ファイルを使ってgrepで新旧を入れ替えてます。

Q3

kanjiファイルから日本語にない漢字を取り出す問題。

$ cat Sh*/vol.57/kanji | uniname | grep Unknown | awk '{print $7}' | paste -sd ''
无产阶级简华历场协议

uninameで調べたら日本語にない漢字はUnknownで絞り込めることが分かったのでそれを利用しました。

Q4

繫体字を簡体字にする問題。

$ cat Sh*/vol.57/ho* | hanzi-convert -s -

hanziconvが便利です

github.com

Q5

以下のような./a.shを作って

#!/bin/bash

date
sleep 30
date

1つ目と2つ目のdateの間を40秒以上にする問題。

$ ./a.sh &;x=$!;sleep 1s;kill -STOP $x;sleep 40;kill -CONT $x

killで止める案を見て良いな~と思ったので採用しました。

Q6

echo うんこ > unkoをしてからgrep うんこ unko | cat >> unkoを繰り返しているといつしか止まらなくなるので、これを止める問題。

$ echo > unko

unkoファイルを空にしてしまえば止まります。

Q7

因数分解する問題。どういう…どういうことなのですか…

$ echo 'factor(x^8 - y^8);' | maxima

Maxima 5.43.2 http://maxima.sourceforge.net
using Lisp GNU Common Lisp (GCL) GCL 2.6.12
Distributed under the GNU Public License. See the file COPYING.
Dedicated to the memory of William Schelter.
The function bug_report() provides bug reporting information.
(%i1)                                          2    2    4    4
(%o1)                - (y - x) (y + x) (y  + x ) (y  + x )
(%i2) %

maximaというツールがあるのでそれでやると楽とのこと。いろんなツールがあるんだなあ…

Q8

循環小数である7/17の最短の循環部分を求める問題。

$ echo -e 'scale=60\n7/17' | bc | grep -oPe '(\d+?)\1' | head -n1 | awk '{print substr($0,0,length($0)/2)}'
4117647058823529

正規表現でひっかければ良い。こういう時後方参照がさっと扱えるようになると楽ですよね~。

LT

今回もLTしました。

www.slideshare.net

12月ということでまたナベを使って何かしました。なんだこれは…。

おわり

今回もとても楽しかったです!午前回があると学びがさらに多くて良いですね…!次回も楽しみです。企画・開催ありがとうございました!