【自習】第65回シェル芸勉強会【この夏はsed、awk、grepに強くなる】
今年の夏に入ってから数日に一回の割合で体調を崩しているので、今回も自宅で配信録画を見ながら深夜の一人勉強会となりました。
- 名称: jus共催 第65回急な告知でもうしわけシェル芸勉強会
- 開催日: 2023年7月22日(土)
- アナウンスページ:
- 東京会場: https://usptomo.doorkeeper.jp/events/159856
- 大阪サテライト会場(開催中止): https://shellgei-osaka.connpass.com/event/290111/
- 福岡サテライト会場: https://shellgei-fukuoka.connpass.com/event/290105/
- 福岡サテライト長崎分室会場: https://shell-nagasaki.connpass.com/event/281459/
- Youtubeライブ配信録画:
- Togetterまとめ: https://togetter.com/li/2191758
- リンク集: https://b.ueda.tech/?post=shellgei_65_link
-
問題で使われているデータファイルの入手方法:
git clone https://github.com/ryuichiueda/ShellGeiData.git
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
次の例のように、sed
のs/正規表現/置換文字列/<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
次の例のように、perl
のx
演算子を使うと、文字列を指定した回数分繰り返すことができます。
$ 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