2016年10月27日

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

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

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


実行環境

  • 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

  • curl 7.50.3


シェル芸演習

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

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

問1

グルーピングされたファイルから、グループ毎に最大の数値を求める問題。

awkの特殊変数FILENAMEを利用し、正規表現でグルーピング部分を切り出して利用する。
後は、グループ毎に最大値を求めるだけ。

01 #!/bin/sh
02
03 gawk '{print gensub(/-.*$/, "", "g", FILENAME), $0}' ./*     |
04 sort -k 1,1 -k 2,2nr                                         |
05 awk '$1 != group{print; group=$1}'

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

gawk '{print gensub(/-.*$/, "", "g", FILENAME), $0}' ./* | sort -k 1,1 -k 2,2nr | awk '$1 != group{print; group=$1}'

問2

アンサイクロペディアの記事から、特定のシェル芸を抽出して実行する問題。

HTMLソースから該当する部分を削り出していき、最後にshに入力するだけ。

01 #!/bin/sh
02
03 curl -s http://ja.uncyclopedia.info/wiki/%E3%82%B7%E3%82%A7%E3%83%AB%E8%8A%B8     |
04 grep -A 4 'カースト最上位者が日常的に書く、素数を出力するワンライナー'            |
05 sed -n '4{s/^[$] //; p}'                                                          |
06 sh

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

curl -s http://ja.uncyclopedia.info/wiki/%E3%82%B7%E3%82%A7%E3%83%AB%E8%8A%B8 | grep -A 4 'カースト最上位者が日常的に書く、素数を出力するワンライナー' | sed -n '4{s/^[$] //; p}' | sh

問3

奇数、偶数毎に、異なった方法でソートする問題。

奇数、偶数が混じった状態でソートするのではなく、それぞれ個別にソートした後、結合するのがポイント。

01 #!/bin/bash
02
03 paste <(cat Q3 |awk '$0%2==1' |sort -n) <(cat Q3 |awk '$0%2==0' |sort -nr) |
04 awk '$1=$1'

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

paste <(cat Q3 | awk '$0%2==1' | sort -n) <(cat Q3 | awk '$0%2==0' | sort -nr) | awk '$1=$1'

問4

自分の端末以外をkillする問題。

環境的に怖くて試せなかったので、本当にこれで正解なのかは分からない…​

01 #!/bin/sh
02
03 (tty; ps aux |awk '$7 ~ /pts\/[0-9]*/')     |
04 awk 'NR==1{sub(/\/dev\//, "", $0); tty=$0} NR!=1 && $7 !~ tty{print $2}' #| xargs sudo kill

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

(tty; ps aux |awk '$7 ~ /pts\/[0-9]*/') | awk 'NR==1{sub(/\/dev\//, "", $0); tty=$0} NR!=1 && $7 !~ tty{print $2}' #| xargs sudo kill

問5

任意の自然数2つについて、最大公約数を求める問題。

手前味噌だけれど、Project Eulerの この問題[http://qiita.com/gin_135/items/f633fa0e0897878fcd10] を参考にして、最大公約数を求めれば良い。

01 #!/bin/sh
02
03 echo $((RANDOM % 100)) $((RANDOM % 100))     |
04 tr ' ' '\n'                                  |
05 sort -nr                                     |
06 xargs                                        |
07 awk 'func gcd(m,n){if(n==0){print m; exit} gcd(n,m%n)} {print $1,$2; gcd($1,$2)}'

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

echo $((RANDOM % 100)) $((RANDOM % 100)) | tr ' ' '\n' | sort -nr | xargs | awk 'func gcd(m,n){if(n==0){print m; exit} gcd(n,m%n)} {print $1,$2; gcd($1,$2)}'

問6

ファイルに記載されている人名について、それぞれ行列番号を求める問題。

awkの連想配列を活用して、ざっくりと。

01 #!/bin/sh
02
03 cat Q6                                                                   |
04 nl                                                                       |
05 tr ' ' ' '                                                              |
06 awk '{for(i=2;i<=NF;i++){print $i,$1,i-1}}'                              |
07 sort -k 1,1 -k 2,2n                                                      |
08 awk '{mem[$1]=mem[$1]" "$2" "$3} END{for(n in mem){print n,mem[n]}}'     |
09 awk '{print $1,$3,$5}'

Tukubai版。大まかな方針は、上記の方法と同じ。

01 #!/bin/sh
02
03 cat Q6                                          |
04 nl                                              |
05 tr ' ' ' '                                     |
06 awk '{for(i=2;i<=NF;i++){print $i,$1,i-1}}'     |
07 sort -k 1,1 -k 2,2n                             |
08 yarr num=1                                      |
09 awk '{print $1,$3,$5}'

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

cat Q6 | nl | tr ' ' ' ' | awk '{for(i=2;i<=NF;i++){print $i,$1,i-1}}' | sort -k 1,1 -k 2,2n | awk '{mem[$1]=mem[$1]" "$2" "$3} END{for(n in mem){print n,mem[n]}}' | awk '{print $1,$3,$5}'
cat Q6 | nl | tr ' ' ' ' | awk '{for(i=2;i<=NF;i++){print $i,$1,i-1}}' | sort -k 1,1 -k 2,2n | yarr num=1 | awk '{print $1,$3,$5}'

問7

「魚」の部分を持つ漢字を列挙する問題。

網羅しているとは言えないけれど…​ 沢山列挙されているWebサイトから取得する。

01 #!/bin/sh
02
03 curl -s http://rtk.art.coocan.jp/cjk/rads/195.html     |
04 grep -o 'TITLE="[^ "]* '                               |
05 sed 's/.*【\(.\)】/\1/'

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

curl -s http://rtk.art.coocan.jp/cjk/rads/195.html | grep -o 'TITLE="[^ "]* ' | sed 's/.*【\(.\)】/\1/'

問8

漢数字をアラビア数字に変換する問題。

単純そうに見えて、実はかなり厄介な問題。
漢数字とアラビア数字では、位取り記法が異なるので、それを変換しつつ、漢字を数字に変換する。

01 #!/bin/sh
02
03 cat Q8                                                         |
04 sed 'y/一二三四五六七八九/123456789/'                          |
05 sed 's/.$/@&/'                                                 |
06 tr -d '\n'                                                     |
07 grep -o .                                                      |
08 sed '/@/{N; s/\n//}'                                           |
09 sed 's/十/1*10/; s/百/1*100/; s/千/1*1000/; s/万/1*10000/'     |
10 xargs                                                          |
11 sed 's/@. /&\n/g'                                              |
12 sed 's/\(.\) \(1\*\)/\1*\2/g'                                  |
13 tr ' ' '+'                                                     |
14 sed 's/@\(.\)+*$/\1/g'                                         |
15 bc

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

cat Q8 | sed 'y/一二三四五六七八九/123456789/' | sed 's/.$/@&/' | tr -d '\n' | grep -o . | sed '/@/{N; s/\n//}' | sed 's/十/1*10/; s/百/1*100/; s/千/1*1000/; s/万/1*10000/' | xargs | sed 's/@. /&\n/g' | sed 's/\(.\) \(1\*\)/\1*\2/g' | tr ' ' '+' | sed 's/@\(.\)+*$/\1/g' | bc

雑記

  • わん!

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