日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です。微力ながら共著させていただいた「シェル・ワンライナー160本ノック」をよろしくお願い申し上げます。

「第65回シェル芸勉強会」リモート参加レポート

2023-07-22(土)「第65回シェル芸勉強会」が開催されました。まずは勉強会主催者の上田さんへ、問題作成と勉強会運営ありがとうございました。参加者の皆様もお疲れ様でした。東京と福岡サテライト、長崎サテライトで会場が設けられました。福岡サテライトは会場にプロジェクターが無かったため、各参加者毎でyoutube動画を見ながら問題に取り組みました。

勉強会情報

勉強会主催者の上田さんが公開されているリンク集をご覧ください。

シェル芸勉強会リンク集

b.ueda.tech

今回から解答例の投稿などは、TwitterではなくMisskeyを使うようになりました。ただTwitterを完全には無くしていないので、Togetterまとめは作っておきました。

Togetterまとめ

togetter.com

勉強会の内容について

今回の問題は前半が割とオーソドックスに感じるもので、後半は色々と調査が必要なものでした。

問題と解答

問題に使うデータファイルは、出題者上田さんが公開されている下記Gitリポジトリから取得してください。

$ git clone https://github.com/ryuichiueda/ShellGeiData.git
$ cd ShellGeiData/vol.65

問題や解答例は、TogetterまとめやYoutubeライブ配信の録画を参照ください。以下、私なりに考えた解答などを書きました。自分が考える解答例は、割とawkのループを使った物に偏りやすい気がしました。ChatGPTを使った解答例も出てきましたね。

Q1

1列目をキーにして、データを縦に並べ替える問題。ぐれさん作のezgactを使った解答が見られました。 github.com

最初に思いついたのはawkのforループを使った解答でしたが、問題でループは封じられておりました。

$ cat input 
a bb ccc dddd
eeee fff gg
h ii

#【awkのforループを使ったダメな解答】
$ cat input |  awk '{for(i=2;i<=NF;i++){print $1" "$i" "}}'
a bb 
a ccc 
a dddd 
eeee fff 
eeee gg 
h ii 

出題者上田さんによる解答例は、行頭に印をつけたあとで縦一列に並べ替え、印を見つけたら変数に代入して1列目として出力する方法でした。例によってebanさんからは面白い解答も出ていました。

Q2

最後の列のデータを、同じ行の列間に挿入する問題。

ebanさんの解答をベースにした解説が面白くて、出題者上田さんから解説をしていただきました。awkのパターンでNF--を使うのは思いつきませんね。

awkのforループを使った解答を考えました。各列の後に最終列($NF)を挿入しながら出力する、地道であまり芸が無い解答です。行末に余分な空白が出来るので、最後にsedで除去しています。

$ cat input | awk '{for(i=1;i<=NF-1;i++){printf $i" "$NF" "}print ""}' | sed 's/ $//'
a dddd bb dddd ccc dddd
eeee gg fff gg
h ii

Q3

ランレングス圧縮の問題でした。ランレングス圧縮とは、同じ文字が連続していたら、連続する回数に変換してデータ量を減らす方法です。

ランレングス圧縮は、以前ぐれさんがTwitterで出したシェル芸問題ネタを解いた経験があったので、やり方はすぐに思い出しました。縦一列に並べ替え、uniq -cで重複の数をカウントする方法です。

$ cat eeeeee 
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaawawawawwwwwwwwwwwwwwwwaaaaaaaaaaiiiiiiiiiiiiiiiiiiiyyyyyyyyyyyyyyyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaddddddddddddddddddaaaaaaaaaaaaaaaaaaa

#【`grep -o`で縦一列に並び替え、`uniq -c`で重複数を数える。】
$ cat eeeeee | grep -o . | uniq -c
     33 a
      1 w
      1 a
      1 w
      1 a
      1 w
      1 a
     16 w
     10 a
     19 i
     15 y
     30 a
     18 d
     19 a

#【文字→重複数の順に並べ替え、1行にして完成。】
$ cat eeeeee | grep -o . | uniq -c | awk '{print $2,$1}' | tr -d ' \n'
a33w1a1w1a1w1a1w16a10i19y15a30d18a19

Q4

同じ矢印状の記号を使って、異なる行の文字を連結する問題。

矢印を使って並べ替えたりする方法をゴニョゴニョ考えたがギブアップでした。 出題者上田さんによる最初の解答例は、awkでキーを元に連結する方法でした。 2つめの解答はデータが無い部分を補って並べ替える方法で、上田さんはこちらの方が好みとのことです。 データが無い部分を補えば良かったんですね。

Q5

1列目と2列目の全ての組み合わせを作って、単語一覧表にマッチする場合のみを抽出すれば良いと考えました。

組み合わせを作るにはjoin -j9(9は存在しない列番号ならなんでも良い)を使ったFULL JOINを使う方法があります。1列目と2列目を別々のデータとして指定するのに、プロセス置換を使う方法を思いつけなかったのが残念不覚でした。単語一覧表は/usr/share/dict/american-englishなどを使えばよいんですね。grepで行完全一致の-xオプションがあるのはすっかり忘れていました。

またlookコマンドを使う解答例もありました。ああ、lookコマンドの存在を忘れていましたね。

Q6

漢字の画数を求める問題。漢字画数情報を探す調査をする時間になりました。

Unihan DatabaseのkTotalStrokesという画数データや、Python漢字ライブラリ、その他のデータが紹介されました。漢字データベースが見つかったら、あとは漢字自体やコードポイントをキーにして、画数データをJOINすればいいですね。頑張りましょう。

Q7

Q6の応用問題で、画数の合計画素数の行のみを出力。

素数の判定は例によってfactorコマンドを使えばよいですね。漢字データベースは下記をダウンロードして利用させていただいきました。

github.com

bashのwhileループで1行ずつ処理する解答を考えました。

#【漢字データベースをダウンロード】
$ wget https://raw.githubusercontent.com/cjkvi/cjkvi-tables/master/joyo2010.txt

$ cat S*/vol.65/kim | while read kim; do [ $(echo $kim | grep -o . | LANG=C sort | LANG=C join - <(awk '{print $1,$3}' FS='\t' joyo2010.txt | LANG=C sort) | awk '{sum+=$2}END{print sum}' | factor | awk 'NF==2' | wc -l) -eq 1 ] && echo $kim; done
金正日
金正恩

終わりに

久しぶりに福岡サテライト会場を開催しましたが、会場にプロジェクターが無いのに前日気がついた。参加者は僕ともう1人だけでしたので、それぞれのパソコンでYoutubeライブ動画を見ながらの参加となりました。

シェル芸勉強会福岡サテライトを開催する会場の候補は、私が知っている限りでは2箇所あります。会場にプロジェクターを寄付した方によると、今回利用した会場はプロジェクターの維持管理がうまく行っていないようでした。もう一つの会場は、数人で利用する規模の会議室は2時間以上の利用が出来なくなっていました。プロジェクターとWi-Fiネット回線が利用出来て、4時間ほど連続利用出来る会場を改めて探した方が良いかもしれません。今後開催出来るかどうか、ちょっと難しくなってきました。