シェル芸スパルタン演習(第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
usp Personal Tukubai 20160402 ( Open usp Tukubaiで代用可能)
シェル芸演習
問題文は、以下の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/:$//'
雑記
にゃーん。
お昼頃のひなたさん。ツシマヤマネコ舎、特にひなたさんの部屋は、時間の流れがゆっくりな気がする。まったり、まったり。 2016.07.13(水) #東山動物園 #ツシマヤマネコ ひなた pic.twitter.com/X3Wg8Mu7Hj
— ginjiro (@gin_135) 2016年8月2日