【自習】第65回シェル芸勉強会【この夏はsed、awk、grepに強くなる】

今年の夏に入ってから数日に一回の割合で体調を崩しているので、今回も自宅で配信録画を見ながら深夜の一人勉強会となりました。



Q1 (@yabeenicoさんの出題 改)

つぎのようなファイルinputについて、

a bb ccc dddd
eeee fff gg
h ii

つぎのような出力を得てください。inputで1列目だったデータが、その行のうしろのデータと対になっています。tarrを使えると一発で解答できますが、別の方法を考えてみてください。できるひとはawk等でループを使わないでください。

a bb
a ccc
a dddd
eeee fff
eeee gg
h ii

A1

$ sed -E 's/([^ ]+ )(.+)/echo "\1"{\2}/;s/ /,/3g' input | bash | tr -d {} | xargs -n2
a bb
a ccc
a dddd
eeee fff
eeee gg
h ii

次の例のように、seds/正規表現/置換文字列/<N>gで、正規表現にマッチした部分についてN回以降マッチした部分全てに置換を行うことができます。

$ echo 0123456789 | sed 's/[0-9]/💩/6g'
01234💩💩💩💩💩

Q2

同じくファイルinputから、最後の列のデータをそのまえのデータに挿入して、

a dddd bb dddd ccc dddd
eeee gg fff gg
h ii

を出力してください。

A2

$ awk '{OFS=" " $NF " ";$1=$1;print}' input | sed 's/ [^ ]\+$//'
a dddd bb dddd ccc dddd
eeee gg fff gg
h ii

Q3

つぎのファイルeeeeeeをランレングス圧縮してください(例: aabbbc -> a2b3c1)。そして、もとに戻してください。

ファイルの中身:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaawawawawwwwwwwwwwwwwwwwaaaaaaaaaaiiiiiiiiiiiiiiiiiiiyyyyyyyyyyyyyyyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaddddddddddddddddddaaaaaaaaaaaaaaaaaaa

A3

ランレングス圧縮:

$ grep -o . eeeeee | uniq -c | awk '{printf $2$1}END{print ""}'
a33w1a1w1a1w1a1w16a10i19y15a30d18a19

uniq -cで、各文字の連続出現回数が得られることを知っていれば、この問題を解くのはかなり楽になるでしょう。

もとに戻す:

$ echo a33w1a1w1a1w1a1w16a10i19y15a30d18a19 | sed -E 's/([a-z])([0-9]+)/\1 \2\n/g' | perl -aE 'print $F[0]x$F[1];END{say}'
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaawawawawwwwwwwwwwwwwwwwaaaaaaaaaaiiiiiiiiiiiiiiiiiiiyyyyyyyyyyyyyyyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaddddddddddddddddddaaaaaaaaaaaaaaaaaaa

次の例のように、perlx演算子を使うと、文字列を指定した回数分繰り返すことができます。

$ perl -E 'say "教育"x13'
教育教育教育教育教育教育教育教育教育教育教育教育教育

Q4

次のsymbolsから、

a->
c-)
g~>
>-z->
)-d-)
>~y~>
)-e
>-f
>~i~>
>~j

つぎの出力を得てください。つまり、同じ矢印状の記号を持つアルファベットを連結してください。

c-))-d-))-e
a->>-z->>-f
g~>>~y~>>~i~>>~j

A4

$ for p in $(echo -e '-\)|\)-\n->|>-\n~>|>~'); do grep -P -- $p symbols | tr -d '\n'; echo; done
c-))-d-))-e
a->>-z->>-f
g~>>~y~>>~i~>>~j

Q5

次のファイルwordsについて、1列目と2列目を組み替えて単語を作ってください。

ang ker
ora ch
pea eat
spea nge
rep le

A5

$ for p in $(awk -v b=$(awk '{printf $2","}' words | sed 's/^/{/;s/,$/}/') '{printf "echo "$1b";"}' words | bash | xargs -n1); do grep "^$p$" /usr/share/dict/words; done
angle
orach
orange
peaker
peach
speaker
repeat

ちなみに、ファイル/usr/share/dict/wordsは、ある特定のファイルの各行のうち、指定した文字列で始まる行を表示するためのコマンドlookが使用するというのですが、このコマンドの使いどころが残念ながら私にはいまひとつよく分かりません。


Q6

echo 焼肉定食から始めて、焼肉定食の画数を求めてください。画数のデータはインターネット上にいろいろ提供されています。(もしかしたらシステム内にもあるかもしれません。)

A6

$ echo 焼肉定食 | grep -o . | while read k; do nkf /usr/share/edict/kanjidic | awk -v k=$k '$1==k{print $6}'; done | sed -z 's/S//g;s/\n/+/g;s/+$/\n/' | bc
35

なお、ファイル/usr/share/edict/kanjidicがない場合は、

sudo apt install kanjidic

でインストールできます(例はDebian系)。


Q7

次のファイルkimから、総画数が素数の金を出力してください。 https://raw.githubusercontent.com/cjkvi/cjkvi-tables/master/joyo2010.txt は適当なディレクトリ(解答例では~/tmp)にダウンロードしてお使いください。なお、joyo2010.txtには、kimに含まれる漢字がすべて含まれています。

金正日
金正恩
金正男
金日成
金与正

A7

$ while read l; do echo $l | grep -o . | while read k; do awk -v k=$k '$1==k{printf $(NF-2)"+"}' ~/tmp/joyo2010.txt; done; echo; done <kim | sed 's/+$//' | bc | factor | paste kim - | awk 'NF==3{print $1,$3}'
金正日 17
金正恩 23
Written on July 31, 2023