シェル芸スパルタン演習(第06回シェル芸勉強会編)
ここ最近、Unixシェルに触る機会が減っており、CLIを使う能力の低下を感じていた。
また、近日中に、第25回シェル芸勉強会の開催が予定されており、参加するにあたり予習をしたいとも考えていた。
そこで、過去に開催されたシェル芸勉強会の問題を活用し、シェル芸力をつける演習をおこなうことにした。
今回は、問題として、第06回シェル芸勉強会の設問を用いた。
実行環境
Arch Linux 4.8.4-1-ARCH
GNU bash 4.3.46
GNU coreutils 8.25-2
GNU diffutils 3.5-1
GNU findutils 4.6.0-2
util-linux 2.28.2-1
grep (GNU grep) 2.26
GNU bc 1.06.95
sed (GNU sed) 4.2.2
gawk 4.1.4
curl 7.50.3
poppler 0.47.0-1
usp Personal Tukubai 20160402 ( Open usp Tukubaiで代用可能)
シェル芸演習
問題文および模範解答は、以下のURLから。
https://www.slideshare.net/ryuichiueda/20130824
問1
[問題1:文字の選別] 次の文字列について、濁点の文字、半濁点の文字、その他の文字(記号含む)の数を数えてください 「どんどこどん、ぱらぱっぱ、ぴろぴろぴー、すっとこどっこい。」 (注意:特に意味はない。)
初っ端から難易度が高い問題。
Tukubai無しだとちょっと分からないので、いきなりTukubaiを解禁。
まず、文字列を列行変換した後、半角カナに変換する。
そして、1レコードあたり1文字ではなく2文字あるものが、濁点・半濁点を含んだ文字になる。
あとは、集計するだけ。
01 #!/bin/sh
02
03 echo どんどこどん、ぱらぱっぱ、ぴろぴろぴー、すっとこどっこい。 |
04 grep -o . |
05 nkf --katakana |
06 han |
07 awk -vFS= 'NF==1&&$0="a"; NF==2&&$0=$2' |
08 sort |
09 uniq -c
以下、コピペ実行用のワンライナーコード。
echo どんどこどん、ぱらぱっぱ、ぴろぴろぴー、すっとこどっこい。 | grep -o . | nkf --katakana | han | awk -vFS= 'NF==1&&$0="a"; NF==2&&$0=$2' | sort | uniq -c
問2
[問題2: 横にcatする] 二つのファイルを横にくっつけてください $ cat fileA 犬もおだてりゃ 論より 年寄りの テキサス $ cat fileB 織田信長 暴力 トライアスロン クローバーホールド $ cat ans 犬もおだてりゃ織田信長 論より暴力 年寄りのトライアスロン テキサスクローバーホールド
問題文の通り、横にcatする問題。
joinコマンドを利用して横結合すると、簡単。
ただ、単純に"join fileA fileB"では、キーが無くて結合できない。
そこで、A,Bそれぞれに行番号を付加したのち、プロセス置換でjoinに入力する。
01 #!/bin/bash
02
03 join <(cat fileA |nl) <(cat fileB |nl) |
04 awk '$0=$2$3'
以下、コピペ実行用のワンライナーコード。
join <(cat fileA |nl) <(cat fileB |nl) | awk '$0=$2$3'
問3
[問題3:小町算] 次のechoから始めて4つの数字の間に + - * / の符号を入れ、100になる計算式をワンライナーで一つ作ってください $ echo 56 2 8 9
個人的に、第06回で一番難しかった…
全パターンを列挙するのは大変なので、条件を満たす式が生成できるまで、ランダムにパターンを試していく。
01 #!/bin/sh
02
03 echo 56 2 8 9 |
04 xargs yes 2>/dev/null |
05 awk 'BEGIN{srand()} {for(i=1;i<=3;i++){printf("%d ", int(rand()*4))}; print $0}' |
06 awk 'BEGIN{op[0]="+";op[1]="-";op[2]="*";op[3]="/"} {print $4,op[$1],$5,op[$2],$6,op[$3],$7}' |
07 awk '{print "echo -n '\''"$0,"'\''; echo '\''"$0"'\'' |bc"}' |
08 sh |
09 awk '$8==100{print; exit}'
以下、コピペ実行用のワンライナーコード。
echo 56 2 8 9 | xargs yes 2>/dev/null | awk 'BEGIN{srand()} {for(i=1;i<=3;i++){printf("%d ", int(rand()*4))}; print $0}' | awk 'BEGIN{op[0]="+";op[1]="-";op[2]="*";op[3]="/"} {print $4,op[$1],$5,op[$2],$6,op[$3],$7}' | awk '{print "echo -n '\''"$0,"'\''; echo '\''"$0"'\'' |bc"}' | sh | awk '$8==100{print; exit}'
問4
[問題4:ネットから拝借] https://ja.wikipedia.org/wiki/ISO_3166-1 (ISO 3166-1 -- wikipedia)のページから、 アイスランド IS アイルランド IE アゼルバイジャン AZ アフガニスタン AF のように、国名と2レターコードの対のリストを作りましょう
Webページの情報を整形する問題。
curlでHTMLソースを落としてきた後、sed等でゴリゴリと加工していく。
01 #!/bin/sh
02
03 curl -s https://ja.wikipedia.org/wiki/ISO_3166-1 |
04 sed -n '/<table class="sortable wikitable">/,/<\/table>/p' |
05 sed '1d; $d' |
06 tr -d '\n' |
07 sed 's;</tr>;&\n;g' |
08 sed '/<th>/d; /class="anchor"/d' |
09 sed 's;</\{0,1\}[^<]*>;@;g' |
10 sed 's/^[@ ]*//g; s/@@*/@/g;' |
11 awk -F@ '{print $1,$5}'
以下、コピペ実行用のワンライナーコード。
curl -s https://ja.wikipedia.org/wiki/ISO_3166-1 | sed -n '/<table class="sortable wikitable">/,/<\/table>/p' | sed '1d; $d' | tr -d '\n' | sed 's;</tr>;&\n;g' | sed '/<th>/d; /class="anchor"/d' | sed 's;</\{0,1\}[^<]*>;@;g' | sed 's/^[@ ]*//g; s/@@*/@/g;' | awk -F@ '{print $1,$5}'
問5
[問題5:フィボナッチ数] 延々とフィボナッチ数列を出力するワンライナーを記述してください。 ただし桁数が増えてもなるべく正確に。
プログラミングの課題としてお馴染みの問題。
awkでシンプルに。
ただ、桁数が増えると計算できなくなってしまうため、gawk ver 4.0以降で利用できる、MPFR拡張を用いる。
01 #!/bin/sh
02
03 yes 高須クリニック |
04 gawk -M 'BEGIN{a=0; b=1} {c=a+b; print a; a=b; b=c}'
以下、コピペ実行用のワンライナーコード。
yes 高須クリニック | gawk -M 'BEGIN{a=0; b=1} {c=a+b; print a; a=b; b=c}'
問6
[問題6:50音の取得] 以下の出力を得てください。(echo あいうえお・・・以外で) あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん
五十音を出力する問題。
文字を実体参照するのは面倒なので、Wikipediaの「ひらがな」のページから抽出する。
01 #!/bin/sh
02
03 curl -s https://ja.wikipedia.org/wiki/%E4%BA%94%E5%8D%81%E9%9F%B3 |
04 grep '>[あ-ん]</a>、' |
05 sed 's;.*title="\(.\)".*;\1;' |
06 sort |
07 sed '/[ゐゑ]/d' |
08 xargs |
09 tr -d ' '
以下、コピペ実行用のワンライナーコード。
curl -s https://ja.wikipedia.org/wiki/%E4%BA%94%E5%8D%81%E9%9F%B3 | grep '>[あ-ん]</a>、' | sed 's;.*title="\(.\)".*;\1;' | sort | sed '/[ゐゑ]/d' | xargs | tr -d ' '
問7
[問題7:ユニコード一覧作成 ] 下のようなUnicodeとひらがな(U+3041「ぁ」〜U+3094「ゔ」)のリストを作成してください。 この問題もiconvを使います。 U+3041 ぁ U+3042 あ U+3043 ぃ ... U+3092 を U+3093 ん U+3094 ゔ
問6と似た問題。
これも実体参照で出力するのは面倒なので、unicode.orgで公開されている、UTF-8におけるひらがなの仕様を利用する。
仕様はPDF形式なので、popplerツールのpdftotextでテキスト出力した後、上手く整形していく。
01 #!/bin/sh
02
03 curl -s http://www.unicode.org/charts/PDF/U3040.pdf |
04 pdftotext -f 3 -l 3 -layout - - |
05 sed 's/ */ /g' |
06 grep 'HIRAGANA' |
07 grep -o '.... . HIRAGANA LETTER [A-Z][A-Z]\{0,1\}' |
08 sort -k 1,1 |
09 grep -v '309[5-9]' |
10 awk '{print "U+"$1,$2}'
以下、コピペ実行用のワンライナーコード。
curl -s http://www.unicode.org/charts/PDF/U3040.pdf | pdftotext -f 3 -l 3 -layout - - | sed 's/ */ /g' | grep 'HIRAGANA' | grep -o '.... . HIRAGANA LETTER [A-Z][A-Z]\{0,1\}' | sort -k 1,1 | grep -v '309[5-9]' | awk '{print "U+"$1,$2}'
参照Webサイト
http://unicode.org/
→ 問7で利用したサイト。Unicodeの開発・管理をおこなっている非営利団体。
雑記
段々と変態的な問題が増えてきた…
でも、まだまだ先は長いぞ。