2016/10/25

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

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

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


実行環境

  • 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


シェル芸演習

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

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

問1

特定の複数の値を含むキーを探す問題。

awkの連想配列を使ってキー毎に横展開した後、grepでフィルタをする。

01 #!/bin/sh
02
03 cat text                                                          |
04 sort                                                              |
05 awk '{arr[$1]=arr[$1]$2} END{for(n in arr){print n, arr[n]}}'     |
06 grep 'オトン'                                                     |
07 grep 'オカン'

Tukubai版。横展開するyarrを使って、シンプルに。

01 #!/bin/sh
02
03 cat text          |
04 sort              |
05 yarr num=1        |
06 grep 'オトン'     |
07 grep ''オカン

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

cat text | sort | awk '{arr[$1]=arr[$1]$2} END{for(n in arr){print n, arr[n]}}' | grep 'オトン' | grep 'オカン'
cat text | sort | yarr num=1 | grep 'オトン' | grep ''オカン

問2

2つのファイルについて、片方のみに存在するレコード、重複するレコードを、それぞれ出力する。
ファイル名をキーとして追加し、それぞれ集計すればOK。

01 #!/bin/sh
02
03 awk '{print FILENAME,$0}' a b                    |
04 sort -k 2,2                                      |
05 uniq -f 1 -c                                     |
06 awk '$1==1{print $2,$3} 2<=$1{print "c",$3}'     |
07 sort -k 1,1

別解。commコマンド、初めて使ったかも…​

01 #!/bin/bash
02
03 comm <(sort a) <(sort b)                             |
04 sed 's/\t/t/g'                                       |
05 sed '/^[^t]/s/^/a /; /^t[^t]/s/t/b /; /^tt/s//c /'   |
06 sort

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

awk '{print FILENAME,$0}' a b | sort -k 2,2 | uniq -f 1 -c | awk '$1==1{print $2,$3} 2<=$1{print "c",$3}' | sort -k 1,1
comm <(sort a) <(sort b) | sed 's/\t/t/g' | sed '/^[^t]/s/^/a /; /^t[^t]/s/t/b /; /^tt/s//c /' | sort

問3

3つのファイルそれぞれについて、ファイルの中身の数値を足し合わせる問題。

これも、awkの連想配列を使えば、簡単にできる。

01 #!/bin/sh
02
03 grep -o '[0-9] \{0,1\}' {a,b,c}     |
04 awk -F: '{sum[$1]+=$2} END{for(n in sum){print n,sum[n]}}'

Tukubai版。sm2を使って、ファイル名をキーとして、それぞれ集計する。

01 #!/bin/sh
02
03 grep -o '[0-9] \{0,1\}' {a,b,c}     |
04 tr ':' ' '                          |
05 sm2 1 1 2 2

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

grep -o '[0-9] \{0,1\}' {a,b,c} | awk -F: '{sum[$1]+=$2} END{for(n in sum){print n,sum[n]}}'
grep -o '[0-9] \{0,1\}' {a,b,c} | tr ':' ' ' | sm2 1 1 2 2

問4

'x’の文字がある縦軸・横軸を求める問題。

データは表形式になっているので、awkの連想配列を使って疑似多次元配列を作り、判別していく。

01 #!/bin/sh
02
03 cat cross             |
04 awk -vFS= '$1=$1'     |
05 sed '1!{/x/!d}'       |
06 awk 'NR==1{for(i=1;i<=NF;i++){head[i]=$i}} NR!=1{printf("%s-", $1); for(i=2;i<=NF;i++){if($i=="x"){print head[i]}}}'

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

cat cross | awk -vFS= '$1=$1' | sed '1!{/x/!d}' | awk 'NR==1{for(i=1;i<=NF;i++){head[i]=$i}} NR!=1{printf("%s-", $1); for(i=2;i<=NF;i++){if($i=="x"){print head[i]}}}'

問5

2行以上の空行を、1行にまとめる問題。

行番号を第1フィールドに付けた後、第1フィールドをスキップしてuniqすればOK。

01 #!/bin/sh
02
03 cat text                                             |
04 awk '$1 ~ /^..*$/{print NR,$0} $1 ~ /^$/{print}'     |
05 uniq                                                 |
06 awk '{for(i=2;i<=NF;i++){printf $i" "}; print ""}'

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

cat text | awk '$1 ~ /^..*$/{print NR,$0} $1 ~ /^$/{print}' | uniq | awk '{for(i=2;i<=NF;i++){printf $i" "}; print ""}'

問6

チェスボード模様の画像を生成する問題。

分からなかったので、模範解答を見てしまった…​
PGMなんてシンプルな画像フォーマットがあったのね。

01 #!/bin/sh
02
03 yes '0 1 0 1 0 1 0 1'                                    |
04 head -n 8                                                |
05 awk 'NR%2==0{print $2,$3,$4,$5,$6,$7,$8,$1; next} 1'     |
06 sed '1iP2 8 8 1' > hoge.pgm

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

yes '0 1 0 1 0 1 0 1' | head -n 8 | awk 'NR%2==0{print $2,$3,$4,$5,$6,$7,$8,$1; next} 1' | sed '1iP2 8 8 1' > hoge.pgm

問7

同じ文字が含まれているレコードを探す問題。

行番号を付けた後、1文字1レコードに縦展開し、sort, uniqするだけ。

01 #!/bin/sh
02
03 cat chinese_characters                       |
04 awk -vFS= '$1=$1'                            |
05 nl                                           |
06 gawk '{for(i=2;i<=NF;i++){print $1,$i}}'     |
07 LANG=C sort -k 2,2                           |
08 LANG=C uniq -f 1 -D

Tukubai版。縦展開にtarrを使った。ちょっとだけシンプル。

01 #!/bin/sh
02
03 cat chinese_characters     |
04 awk -vFS= '$1=$1'          |
05 rank                       |
06 tarr num=1                 |
07 LANG=C sort -k 2,2         |
08 LANG=C uniq -f 1 -D

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

cat chinese_characters | awk -vFS= '$1=$1' | nl | gawk '{for(i=2;i<=NF;i++){print $1,$i}}' | LANG=C sort -k 2,2 | LANG=C uniq -f 1 -D
cat chinese_characters | awk -vFS= '$1=$1' | rank | tarr num=1 | LANG=C sort -k 2,2 | LANG=C uniq -f 1 -D

問8

重複する数列のうち、最長のものを調べる問題。

良い方法が思い浮かばなかったので、愚直に全パターンを出力し、重複するものを調べた…​

01 #!/bin/sh
02
03 cat number                                                                                   |
04 awk -vFS= '{for(i=1;i<=NF;i++){for(j=1;j<=NF;j++){for(k=j;k<=i;k++){printf $k}; print ""}}}' |
05 sed '/^$/d'                                                                                  |
06 sort -n                                                                                      |
07 uniq -d                                                                                      |
08 awk -vFS= '{print NF,$0}'                                                                    |
09 sort -k 1,1n                                                                                 |
10 awk '{arr[$1]=arr[$1]" "$2} END{for(n in arr){print n,arr[n]}}'                              |
11 sort -k 1,1nr                                                                                |
12 head -n 1

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

cat number | awk -vFS= '{for(i=1;i<=NF;i++){for(j=1;j<=NF;j++){for(k=j;k<=i;k++){printf $k}; print ""}}}' | sed '/^$/d' | sort -n | uniq -d | awk -vFS= '{print NF,$0}' | sort -k 1,1n | awk '{arr[$1]=arr[$1]" "$2} END{for(n in arr){print n,arr[n]}}' | sort -k 1,1nr | head -n 1

雑記

  • にゃーーん。

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