たいちょーの雑記

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

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

しました。

Q1

以下の図形を描く問題。ただし、forwhileは禁止とのこと。ナンテコッタ…

* * * * * * * *
* *         * *
*   *     *   *
*     * *     *
*     * *     *
*   *     *   *
* *         * *
* * * * * * * *
echo 11111111110000111010010110011001 | fold -w8 | sed 'y/10/* /' | pee 'cat' tac | sed 's/./& /g'

方針としては、上4行を取り出し、*10で表現、それをいい感じに置換・成形をします。上4行、つまり上半分だけとしたのは pee cat tac で鏡写しができるからですね。

PowerShellでの解答もやってみます

$lines=(@(1111, 1100, 1010, 1001) | %{ "$_"+[string]::Join("", "$_"[("$_".Length-1)..0]) });@($lines,$lines[($lines.Length-1)..0]) | %{ $_ -replace 1,'* ' -replace 0,'  ' }

方針はほぼ同じですが、01で表現するのは左上の4x4の範囲ですこれを左右反転、上下反転の順番で複製して置換、出力するというものです

Q2

以下のおしゃれなUNKOからASCIIなUNKOに戻す問題。おしゃれなUNKOってなんだろうね

echo 𝒰𝒩𝒦𝒪 
echo 𝒰𝒩𝒦𝒪 | uni i | awk '{print $12}' | tr -d '
' | awk 4

特殊文字を元に戻すとき、uninameなどを使うと楽です。自分はuniを使っています。好きなのを使えばよいです

github.com

PowerShell版もuniで情報を取り出して、切り出すだけなので省略します。

Q3

端末のフチに沿って@を出力する問題。

perl -e "print '@'x$(tput cols)"; yes | head -n"$(tput lines)"|sed "s/y/perl -e \"print '@'.' 'x($(tput cols)-2).'@'\"/e";perl -e "print '@'x$(tput cols)"

tput cols$COLUMNSなどで端末の幅が得られます。これを利用して文字を繰り返すべき回数を得ます。文字の繰り返しにはperlを使いましたがここはなんでも良いと思います

PowerShellでは$Host.UI.RawUIでアクセスできるInternalHostRawUserInterfaceオブジェクトから幅や高さといった情報が得られます。

$ $Host.UI.RawUI


ForegroundColor       : Gray
BackgroundColor       : Black
CursorPosition        : 0,30
WindowPosition        : 0,0
CursorSize            : 25
BufferSize            : 106,31
WindowSize            : 106,31
MaxWindowSize         : 106,31
MaxPhysicalWindowSize : 1904,62
KeyAvailable          : True
WindowTitle           : Windows PowerShell

この値を使って同様に描画していきます。PowerShellでも掛け算で文字の繰り返しを表現できるので以下のような感じで

"@"*$Host.UI.RawUI.WindowSize.Width; 0..($Host.UI.RawUI.WindowSize.Height-2) | %{ "@"+" "*($Host.UI.RawUI.WindowSize.Width-2)+"@" }; "@"*$Host.UI.RawUI.WindowSize.Width;
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@                                                                                                        @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Q4

端末上で💩を対消滅させ、そのエネルギーで🍣を生み出す問題。どういうことなの

(yes '@'{,,,,,,,,,,} | head -n11|awk '{$NR="💩";print}' | sed 'y/@ /__/' | awk '{print $1"  "$1}' | teip -f 2 rev; echo ______________________🍣______________________)|sed 'y/_/ /' | textimg -asl1

ちょっと端末上でというのが抜けていたのです…。方針としては1行ずつ左右から💩を近づけていくように出力し、最終行は🍣を挿入します

(yes '@'{,,,,,,,,,,} | head -n11|awk '{$NR="💩";print}' | sed 'y/@ /__/' | awk '{print $1"  "$1}' | teip -f 2 rev; echo ______________________🍣______________________)|sed 'y/_/ /'
💩                                          💩
  💩                                      💩
    💩                                  💩
      💩                              💩
        💩                          💩
          💩                      💩
            💩                  💩
              💩              💩
                💩          💩
                  💩      💩
                    💩  💩
                      🍣

これをtextimg-aでアニメーションにして画像を出力しています

Q5

wordsファイル内にある各単語について、同じ文字がちょうど3つ存在し、ほかに同じ文字がちょうど2つ存在するものを取り出す問題。前の問題との格差がすごい。

$ cat *55/words | sel 1 1 | teip -sf2 -- zsh -c "grep -o . | sort | uniq -c | sort -rn | tr -d '
'" | grep 3 | grep 2 | sel 1
multidimensional
sharecropper
antenna
constitutionals
Goolagong
nostalgically

selawk {print $HOGE}へのショートカットみたいなコマンドです。

github.com

ここでは awk '{print $1,$1}'の代わりに使っています。そしてteipで2カラム目に文字の集計を列挙します

$ cat *55/words | sel 1 1 | teip -sf2 -- zsh -c "grep -o . | sort | uniq -c | sort -rn | tr -d '
'" 
metabolizes       2 e      1 z      1 t      1 s      1 o      1 m      1 l      1 i      1 b      1 a
Manuela       2 a      1 u      1 n      1 l      1 e      1 M
Kroger       2 r      1 o      1 g      1 e      1 K
purchasable       2 a      1 u      1 s      1 r      1 p      1 l      1 h      1 e      1 c      1 b
luster       1 u      1 t      1 s      1 r      1 l      1 e
clattering       2 t      1 r      1 n      1 l      1 i      1 g      1 e      1 c      1 a
mesh       1 s      1 m      1 h      1 e
campus       1 u      1 s      1 p      1 m      1 c      1 a
seating       1 t      1 s      1 n      1 i      1 g      1 e      1 a
giblet       1 t      1 l      1 i      1 g      1 e      1 b
Tomas       1 s      1 o      1 m      1 a      1 T
hooch       2 o      2 h      1 c
Rivas       1 v      1 s      1 i      1 a      1 R
Django       1 o      1 n      1 j      1 g      1 a      1 D
Alger       1 r      1 l      1 g      1 e      1 A
multidimensional       3 i      2 n      2 m      2 l      1 u      1 t      1 s      1 o      1 e      1 d      1 a
Troilus       1 u      1 s      1 r      1 o      1 l      1 i      1 T
outtake       2 t      1 u      1 o      1 k      1 e      1 a
tries       1 t      1 s      1 r      1 i      1 e
Nobelist       1 t      1 s      1 o      1 l      1 i      1 e      1 b      1 N
...

これで一行に元の単語と単語を構成する文字種とその個数が列挙されます。ここから、3個と2個をgrepすればよいですね。

PowerShellでもやってみます

$ cat .\words |%{ @{t=$_; g=($_.ToCharArray()|group)} } |?{ [System.Linq.Enumerable]::Any($_.g, [System.Func[object,bool]] { param($x) $x.Count -eq 3 }) } |?{ [System.Linq.Enumerable]::Any($_.g, [System.Func[object,bool]]{param($x) $x.Count -eq 2})}|%{$_.t}

Where-ObjectGroup-Objectの組み合わせです。やっていることは先に説明したものと同じです。

Q6

格子状に💩を並べる問題。なんか錯視を起こしそう

yes "echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'" | head -n8 | bash | uniq

Q1の方針と似ています。4x5を作ってから縦横へ展開します。

$ yes "echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'" | head -n8
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'
echo 1000 0101 0010 0101 1000 | fmt -1 | sed 'y/10/💩 /' | sed 's/.*/&&&&&&&&/' | sed -E 's/(.)(.*)/\1\2\1/'

これをbashなどに渡すと

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

という感じになります。よくみると縦方向に重複があるので、uniqで消します

PowerShellでもやってみます

1..8 |%{@("1000","0101","0010","0101","1000")|%{("$_"*8) -replace '^(.)(.*)','$1$2$1' }}|%{"$_" -replace 1,'??' -replace 0,' '} | Get-Unique

Q7

wordsの中からしりとりになっている行を取り出して、一行にする問題。

cat *55/words | sed -z 's/./\L&/g; s/
/ /g' | sed -r 's/(.) \1/\1@\1/g' | fmt -1 | grep @ | tr @ ' '
campus seating giblet tomas
lifeless secures
clews shove exhaust
orifice equivocal

sedを使って一行にしながら、すべてを小文字にします。そのあとスペースをはさんで同じ文字が続く場合に@を挿入します。それらの中から@を含む部分だけを取り出せば、しりとりになりますが…

ただし、もとのテキストはtomasTomasになっているので、この解答ではちょっと不十分ですね…。 Twitterでは以下のようなのを見かけて天才だなあと思いました。

sed -zr 's/(.)
(\1)/\1 \2/ig' *55/words | grep ' '

LT

今回はLTを行いました。owariコマンドをアップデートしましたよという感じの話です。

www.slideshare.net

聞いてくださった方ありがとうございました!

おわり

図形を書く問題は久しぶりな気がしました。やはりなかなか大変ですね…。まずは01で表現するみたいな方針が一致してくるのも面白いなーとおもいました。

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

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

owariコマンドv2を作りました

作りました。それについてのポエムです

owari コマンドとは

xztaityozx.hatenablog.com

Go言語で制作し公開した「終わりに関するアスキーアートを出力する」だけのコマンドです。 例えば以下のような感じのを出すことができます

GitHubの表示を見るに、公開からだいたい3年ぐらい経っていそうなんですが、今でも時々使ってもらっているのを見るとうれしくて小躍りしてしまいます。 とくにスライドの最後とかに登場するのを見ると嬉しくて泣いてしまいますね。

公開後いくつかのPRが来ました。色をパイプ先にも強制するもの(#5)や、掃除・ドキュメント整備(#16, #8)などがありました。中には無限ヵ月放置してしまっていたやつもあり、マジすいませんという感じです(#38)。 ともかく、自分のリポジトリのなかでは一番PRが来たものとなりました。大感謝です。

そんなowariコマンドですが、最近v2をリリースしました。v2としたのは実装がごろっと変わったからです。それ以外の理由はないです

v2.0について

忍法、ReleaseのMarkdownコピペの術

  • 設計を見直しました
    • 複数のフォントを切り替えられるようにしました(--font)
      • kanbanサブコマンドに追加された、-t, --textimgオプションは--font=NotoSansCJKへのShorthandとなりました
  • owari boomサブコマンドがDeprecatedになりました。ご利用ありがとうございました。
  • 表示幅を指定する--widthオプションがDeprecatedになりました。
  • kanbanサブコマンドで、ギコ猫の表示位置を棒の右側にすることができるようになりました(--reverse)
  • kanbanサブコマンドで、ギコ猫を二匹に増やすことができるようになりました(--twin)
  • --overwriteオプションのShorthandとして-oを追加しました
  • --colorfulオプションのShorthandとして-cを追加しました
  • --durationオプションのShorthandとして-dを追加しました
  • NHKと表記していたところをOWRに変えました
  • シェル芸Bot対応として入れられていた1行目の空白をオプションに切り出しました(-E, --insert-empty, default: true)

v2.0にするきっかけ

1つ目の理由としては、先日来たこのIssueへのコメントです。kanbanサブコマンドの持ち手部分がずれているのが気になって建てたIssueでしたが、どう直そうか考えているうちに忘れたものでした。コメントにより思い出し修正を行いました。

しかし、修正は思った通りになりませんでした。kanbanサブコマンドで使っているдといった文字が、フォントなどの描画される環境によって文字幅が1になったり2になったりするからです。議論の中で特定のフォント(NotoSansCJK-Regularとか)をデフォルトとして出力する案も出ました。しかしそれは今の状態と何が違うのだろうとも思いました。結局、このIssueでは表示したいフォントにあわせて出力するようなオプションを追加することを提案しました(#43)。

さてここから気になるのが「ほかのフォントに対応したい」という要望に対する解答です。同様にオプションを追加する提案を続ければ、いずれ収拾がつかなくなることは目に見えていました。何か別の方法が必要でした。いろいろ考えているうちにモチベがもりもりになって、気づいたらブランチを切っていました。考えるきっかけをくれた@eggplantsさん、本当にありがとうございました。

実装の方は、単純にJSONにテンプレをいくつか用意すればよくない?と思ったので、いくつかのテンプレの中から名前でそれらを切り替えるようにしました。

{
 "Giko": {
    "default": [
      "∧∧   ||        ",
      "( ゚д゚)||          ",
      "/    づΦ       "
    ],
    "NotoSansCJK": [
      "∧ ∧   ||      ",
      "( ゚д゚)||         ",
      "/   づΦ        "
    ]
  },
}

こんな感じです。これらは--fontオプションを通じて切り替えができます。先の議論で追加されたオプションは--font=NotoSansCJKへのショートカットになっています。

JSONの読み込みは、Go 1.16 で追加されたgo:embedを使いました。ファイルの中身などをソースに埋め込む機能ですが、これだ!と思って使ったわけじゃなく「使ってみたかったから使った」ぐらいです。そうです趣味です。go:embedの使い心地は抜群なのですが、ユーザー定義のJSONを読ませたいときにどうすればよいのかわからなくなりました。ワハハ。なんも考えてねえじゃん。

ともあれ、複数のフォント対応をPRで受け付けることが可能になりました。うれしい。

ちなみにすべてのサブコマンドで利用できる実装にしたのですが、kanbanサブコマンドにしか複数のテンプレが存在しないです。


2つ目としては、PrintUtil.goの存在でした。このPrintUtil.goにはアスキーアートの出力に関する処理が書かれており、各サブコマンドで共通して使っているものでした。PrintUtil.goはUtilの名に恥じない何でも屋で、出力はもちろんのこと、各文字の 見た目上の長さ についての計算なども行っていました。

正直実装自体はさして問題ないなと思っていました。実際v2でも元の実装を再利用した部分があります。では何が問題だったかというと、PrintUtil.goowari/cmd以下に置いてあることです。どうせcmd以下に置くようなサブコマンドの実装でしか使わないのですが、サブコマンドの一覧がcmd以下と一致しないのが気になっていました。そうです。気持ちの問題です。なんというかこっちはついでという感じです。

おわり

owariコマンド自体はアスキーアートを出力するだけのコマンドなのですが、実際に使ってみると割と使いどころがあります。冒頭にも書きましたが、スライドの最後の一枚に配置したり、重い処理の後ろに && owariとくっつけたりなどです。自分は owari big -con infで大きな「終」をイルミネーションにするのも好きです。 インストールもgo get -u github.com/xztaityozx/owari するだけですし、ビルド済みのバイナリがよければ、GitHub Releaseに公開しています。ぜひ使ってみてくださいね。

最後にowariは無限にアスキーアートを募集しています。「終わり」っぽかったら何でもよいのでPRやIssue投げつけてください!

$ owari kanban -ga xztaityozx

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

うむ…ずれるな…

雑記 2021/08/07

雑記

渾身のコミットがCIのLinterに弾かれて泣いちゃった

ネトゲ

ネトゲに昔ハマっていたのだけどWin機の調子が悪くなって満足にできなくなったので、やらなくなっていた。ところが最近金を手に入れたのでWin機を新しくして復帰した。 本当はC#をガンガン書きたかったから買い換えたのであって、ネトゲに復帰するために買いなおしたわけじゃないんだけどな…。と思いつつ、昔楽しかったこととかも思い出してそれなりの時間プレイしちゃってる。楽しいねやっぱり

ただアカウントを新しくしたこともあり、完全にソロゲーとなっている。ネトゲらしさは他人と取引ができるぐらい。

運動

飽きずに筋トレしてたらなんか痩せてきた。明らかに浮き輪肉とかが減った。なんかうれしいね。

おわり

そろそろアウトプットできそうなことがあるのでそれについて書けたらいいね

第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スクリプトを生成するっていうのはいかにもシェル芸的なので楽しいですね。でも、あんまり普段はやらない作業かもなぁと思いました。文章の修正とかをする人ならよくあるのかな?って感じです。

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