2016年10月18日

シェル芸スパルタン演習(第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


シェル芸演習

問題文および模範解答は、以下の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/:$//'

雑記

  • オオカミ可愛い!!!!!(書くネタが無い)

Tags: シェル芸 Unix
このエントリーをはてなブックマークに追加