2016年10月24日

シェル芸スパルタン演習(第17回シェル芸勉強会編)

ここ最近、Unixシェルに触る機会が減っており、CLIを使う能力の低下を感じていた。
また、近日中に、第25回シェル芸勉強会の開催が予定されており、参加するにあたり予習をしたいとも考えていた。
そこで、過去に開催されたシェル芸勉強会の問題を活用し、シェル芸力をつける演習をおこなうことにした。

今回は、問題として、第17回シェル芸勉強会の設問を用いた。


実行環境

  • 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

  • sed (GNU sed) 4.2.2

  • gawk 4.1.4


シェル芸演習

問題文は、以下のURLから。
https://blog.ueda.asia/?p=6454

模範解答は、こちらから。
https://blog.ueda.asia/?p=6409

問1

第1フィールドをキーとして、データを横展開する問題。
awkの連想配列を使えば、簡単にできる。

01 #!/bin/sh
02
03 cat data1                                                                   |
04 sort                                                                        |
05 awk '{printf("%s", $1==o1 ? "" : "\n"$1" "); printf("%d ", $2); o1=$1}'     |
06 awk '1'                                                                     |
07 sed '/^$/d'

Tukubai版。横展開するコマンド、yarrを使えば、より簡単に。

01 #!/bin/sh
02
03 cat data1     |
04 sort          |
05 yarr num=1

JSON形式。
複雑だけれど、元のデータ構造を考慮しつつ、少しずつJSON形式に整形していけばOK。

01 #!/bin/sh
02
03 cat data1                                                                                                   |
04 sort                                                                                                        |
05 awk '{printf("%s", $1==o1 ? "" : "\n"$1" "); printf("%d ", $2); o1=$1}'                                     |
06 awk '1'                                                                                                     |
07 sed '/^$/d'                                                                                                 |
08 awk 'BEGIN{print "{"} {print $1":["; for(i=2;i<=NF;i++){printf("%d,", $i)}; print "],"} END{print "}"}'     |
09 tr -d '\n'                                                                                                  |
10 sed 's/,]/]/g'                                                                                              |
11 sed 's/,}/}/'                                                                                               |
12 awk '1'

以下、コピペ実行用のワンライナーコード。

cat data1 | sort | awk '{printf("%s", $1==o1 ? "" : "\n"$1" "); printf("%d ", $2); o1=$1}' | awk '1' | sed '/^$/d'
cat data1 | sort | yarr num=1

JSON形式。

cat data1 | sort | awk '{printf("%s", $1==o1 ? "" : "\n"$1" "); printf("%d ", $2); o1=$1}' | awk '1' | sed '/^$/d' | awk 'BEGIN{print "{"} {print $1":["; for(i=2;i<=NF;i++){printf("%d,", $i)}; print "],"} END{print "}"}' | tr -d '\n' | sed 's/,]/]/g' | sed 's/,}/}/' | awk '1'

問2

ファイルの中から、重複レコードを抽出する問題。
ただし、フィールドの並び順は考慮しないので、ちょっと難しい。
各レコード毎に、フィールドを反転させたものも出力し、重複があるかどうかを調べていけば良い。

01 #!/bin/sh
02
03 cat data                                 |
04 awk '{print NR,$0; print "a",$2,$1}'     |
05 sort -k 2,3                              |
06 uniq -f 1 -D                             |
07 awk '$1 !~ "a"'

以下、コピペ実行用のワンライナーコード。

cat data | awk '{print NR,$0; print "a",$2,$1}' | sort -k 2,3 | uniq -f 1 -D | awk '$1 !~ "a"'

問3

JSON形式の要素毎に、数字の合計を求める問題。

方針としては、問1の逆をやれば良い。
JSONのデータ構造を維持しつつ、キー毎にうまく展開すればOK。

01 #!/bin/sh
02
03 cat data                        |
04 sed 's/[]{}],\{0,1\}/&\n/g'     |
05 sed '/^[{}]\{0,1\}$/d'          |
06 sed 's/,$//'                    |
07 tr '":[,]' ' '                  |
08 awk '{sum=0; for(i=2;i<=NF;i++){sum+=$i}; print $1,sum}'

以下、コピペ実行用のワンライナーコード。

cat data | sed 's/[]{}],\{0,1\}/&\n/g' | sed '/^[{}]\{0,1\}$/d' | sed 's/,$//' | tr '":[,]' ' ' | awk '{sum=0; for(i=2;i<=NF;i++){sum+=$i}; print $1,sum}'

問4

前回、第11回の問7, 8とほぼ同じ問題。

なので、そちらを参考にして解く。
http://harpyja.daemon.asia/posts/2016-10-18-shellgei_studying_11

01 #!/bin/sh
02
03 echo '::1'                                                                                          |
04 sed 's/::/:abbr:/'                                                                                  |
05 sed 's/^:/0&/'                                                                                      |
06 awk -F: '{print 8-NF+1; print}'                                                                     |
07 tr ':' '\n'                                                                                         |
08 awk 'NR==1{nos=$0} /abbr/{for(i=1;i<=nos;i++){printf "0:"}; print ""; next} NR!=1{print $0":"}'     |
09 tr -d '\n'                                                                                          |
10 sed 's/:$//'                                                                                        |
11 awk -F: '{for(i=1;i<=NF;i++){printf("%04s:", $i)}; print ""}'                                       |
12 tr ' ' '0'                                                                                          |
13 sed 's/:$//'

以下、コピペ実行用のワンライナーコード。

echo '::1' | sed 's/::/:abbr:/' | sed 's/^:/0&/' | awk -F: '{print 8-NF+1; print}' | tr ':' '\n' | awk 'NR==1{nos=$0} /abbr/{for(i=1;i<=nos;i++){printf "0:"}; print ""; next} NR!=1{print $0":"}' | tr -d '\n' | sed 's/:$//' | awk -F: '{for(i=1;i<=NF;i++){printf("%04s:", $i)}; print ""}' | tr ' ' '0' | sed 's/:$//'

雑記

  • にゃーん。

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