シェル芸スパルタン演習(第11回シェル芸勉強会編)
ここ最近、Unixシェルに触る機会が減っており、CLIを使う能力の低下を感じていた。
また、近日中に、第25回シェル芸勉強会の開催が予定されており、参加するにあたり予習をしたいとも考えていた。
そこで、過去に開催されたシェル芸勉強会の問題を活用し、シェル芸力をつける演習をおこなうことにした。
今回は、問題として、第11回シェル芸勉強会の設問を用いた。
実行環境
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
nkf 2.1.4
mecab 0.996
usp Personal Tukubai 20160402 ( Open usp Tukubaiで代用可能)
シェル芸演習
問題文および模範解答は、以下のURLから。
https://www.slideshare.net/ryuichiueda/20140614-jus-uspstudy
問1
次のechoの出力を、echoにパイプでつなげて足し算をしてください。 $ echo -12,135,123 135,123
文字列を足し算する問題。
余分なカンマを除去した後、加算記号を付けてbcに入れるだけ。
01 #!/bin/sh
02
03 echo -12,135,123 135,123 |
04 tr -d ',' |
05 tr ' ' '+' |
06 bc
Tukubai版。流れは上と殆ど同じ。
01 #!/bin/sh
02
03 echo -12,135,123 135,123 |
04 tr -d ',' |
05 ysum |
06 self NF
以下、コピペ実行用のワンライナーコード。
echo -12,135,123 135,123 | tr -d ',' | tr ' ' '+' | bc
echo -12,135,123 135,123 | tr -d ',' | ysum | self NF
問2
次のメモについて、各レコードが「名前 点数」の順になるようにデータを整形しましょう。 $ cat score 45 鎌田 濱田 72 今泉 84 24 上田 94 斎藤
フィールドの並び間違いを修正する問題。
片方のフィールドが数値であることを利用し、awkのint()を使って判別すればOK。
01 #!/bin/sh
02
03 cat score |
04 awk 'int($1){print $2,$1; next} 1'
以下、コピペ実行用のワンライナーコード。
cat score | awk 'int($1){print $2,$1; next} 1'
問3
m/sに直してください。 1マイル = 1.609 kmで演算を $ cat speed 100km/h 16mph
単位変換の問題。
単位を乗数に置き換えて、計算する。
01 #!/bin/sh
02
03 cat speed |
04 sed '1{s/km/*1000/; s/h/(60*60)/}; 2{s/m/*1.609*1000/; s$ph$/(60*60)$}' |
05 bc -l |
06 sed 's;$; km/s;'
以下、コピペ実行用のワンライナーコード。
cat speed | sed '1{s/km/*1000/; s/h/(60*60)/}; 2{s/m/*1.609*1000/; s$ph$/(60*60)$}' | bc -l | sed 's;$; km/s;'
問4
さいとうさん、さわださん、ひろたさん、いとうさんの数を数えてください。
間違えられやすい人名漢字の中から、同じ読みの人を数を求める問題。
mecabは漢字の読みも出力してくれる事を利用する。
01 #!/bin/sh
02
03 cat name |
04 tr ' ' '\n' |
05 mecab |
06 awk -F, '$0=$8' |
07 sort |
08 nkf --hiragana |
09 sed 's/$/さん/' |
10 uniq -c
以下、コピペ実行用のワンライナーコード。
cat name | tr ' ' '\n' | mecab | awk -F, '$0=$8' | sort | nkf --hiragana | sed 's/$/さん/' | uniq -c
問5
次のCSVに書いてある数字を足し算してください。 $ cat csv 1,2,"123,456",-5,"-123,456" 6,7,8,"12",9
カンマ','がフィールド区切り文字以外に利用されているCSVの中身を解析し、足し合わせる問題。
あまりお行儀が宜しくないけれど、これはCSVの仕様として認められているので、現実にはよくあるケース。
sedのsコマンドと正規表現で、頑張ってフィールド毎に抽出し、足し合わせる。
01 #!/bin/sh
02
03 cat csv |
04 sed 's/"\([-0-9]*\),\{0,1\}\([0-9]*\)"/\1\2/g' |
05 sed '1{N; s/[,\n]/+/g}' |
06 bc
Tukubai版。大まかな流れは、上と同じ。
01 #!/bin/sh
02
03 cat csv |
04 tr ',' '\n' |
05 sed '/"-\{0,1\}[0-9]\{1,\}$/{N; s/\n//}; /"/s/"//g' |
06 xargs |
07 ysum |
08 self NF
以下、コピペ実行用のワンライナーコード。
cat csv | sed 's/"\([-0-9]*\),\{0,1\}\([0-9]*\)"/\1\2/g' | sed '1{N; s/[,\n]/+/g}' | bc
cat csv | tr ',' '\n' | sed '/"-\{0,1\}[0-9]\{1,\}$/{N; s/\n//}; /"/s/"//g' | xargs | ysum | self NF
問6
次のデータを行列として転置してください。 $ cat matrix a b c d e f g h i
行列を転置する問題。
綺麗なフィールド区切りになっているので、awkでループを回して地道に頑張る。
01 #!/bin/sh
02
03 cat matrix |
04 awk '{for(i=1;i<=NF;i++){arr[NR,i]=$i}} END{for(i=1;i<=NR;i++){for(j=1;j<=NF;j++){printf("%s ", arr[j,i])}; print ""}}'
Tukubai版。
Tukubaiには、ずばり転置をおこなうtateyokoコマンドが存在するので、それを実行するだけ。
01 #!/bin/sh
02
03 tateyoko matrix
以下、コピペ実行用のワンライナーコード。
cat matrix | awk '{for(i=1;i<=NF;i++){arr[NR,i]=$i}} END{for(i=1;i<=NR;i++){for(j=1;j<=NF;j++){printf("%s ", arr[j,i])}; print ""}}'
tateyoko matrix
問7
次のIPv6のIPアドレスから、省略された0を復元してください。 4桁の頭のゼロは省略できる。 $ echo 2001:db8:20:3:1000:100:20:3
省略されたIPv6アドレスを、完全な形に復元する問題。
IPv6はフィールド区切り形式になっている事を利用し、IPv6の仕様を読みつつ、復元していく。
01 #!/bin/sh
02
03 echo 2001:db8:20:3:1000:100:20:3 |
04 tr ':' '\n' |
05 awk '{printf("%4s\n",$0)}' |
06 tr ' ' '0' |
07 xargs |
08 tr ' ' ':'
Tukubai版。頭0詰めをするmaezeroコマンドのお陰で、ちょっとだけシンプルに。
01 #!/bin/sh
02
03 echo 2001:db8:20:3:1000:100:20:3 |
04 tr ':' '\n' |
05 maezero 1.4 |
06 xargs |
07 tr ' ' ':'
以下、コピペ実行用のワンライナーコード。
echo 2001:db8:20:3:1000:100:20:3 | tr ':' '\n' | awk '{printf("%4s\n",$0)}' | tr ' ' '0' | xargs | tr ' ' ':'
echo 2001:db8:20:3:1000:100:20:3 | tr ':' '\n' | maezero 1.4 | xargs | tr ' ' ':'
問8
次のIPv6のIPアドレスから、省略された0を復元してください。 - 4桁の頭のゼロは省略できる。 - 1回だけ、:0000:0000:と0000が続くところだけは::と省略できる。 $ cat ipv6 2001:db8::1234:0:0:9abc 2001:db8:20:3:1000:100:20:3 2001:db8::9abc
問7の発展系。
これも、IPv6の仕様として認められているので、現実にありえるケース。
難しいけれど、省略されたフィールドを、地道に復元していく。
01 #!/bin/sh
02
03 cat ipv6 |
04 awk -F: '{print NF,$0}' |
05 sed 's/::/:abbr:/' |
06 tr ':' ' ' |
07 awk '{OFS=":"; for(i=2;i<=NF;i++){if($i=="abbr"){for(j=$1;j<=8;j++){printf "abbr"OFS}}else{printf("%s"OFS,$i)}}; print ""}' |
08 sed 's/abbr/0000/g' |
09 awk -F: '{for(i=1;i<=NF-1;i++){printf("%4s:",$i)}; print ""}' |
10 tr ' ' '0' |
11 sed 's/:$//'
以下、コピペ実行用のワンライナーコード。
cat ipv6 | awk -F: '{print NF,$0}' | sed 's/::/:abbr:/' | tr ':' ' ' | awk '{OFS=":"; for(i=2;i<=NF;i++){if($i=="abbr"){for(j=$1;j<=8;j++){printf "abbr"OFS}}else{printf("%s"OFS,$i)}}; print ""}' | sed 's/abbr/0000/g' | awk -F: '{for(i=1;i<=NF-1;i++){printf("%4s:",$i)}; print ""}' | tr ' ' '0' | sed 's/:$//'
雑記
オオカミ可愛い!!!!!(書くネタが無い)
ティトとノゾミ。兄妹仲良く......やる気がありません(^_^;) ま、まあ、この日は10月なのに妙に暑かったから、仕方ないよね! 2016.10.01(土) #東山動物園 #シンリンオオカミ ティト(左)、ノゾミ(右) pic.twitter.com/1HeFbBuKSP
— ginjiro (@gin_135) 2016年10月7日