シェル芸初心者向け勉強会に後日自宅にてサテライト参加(参加とは言わない)【前半】

シェル芸初心者向け勉強会が福岡であったようなので問題を解いてみました。ウォーミングアップ問題はawkを封じて解いた解法とawkで解いた解法を書きました。因みにtukubaiは全く使えない(僕の技術的に)ので使っていません。

ウォーミングアップ問題1

問題

1から10まで合計を計算してください

解法

seq -s + 10 | bc
seq 10 | awk '{t+=$1}END{print t}'

解説

awk封じでもかなり色々な解法があると思いますが個人的には seq で式を生成してbcに食わせるのが好きです。

awkの方はかなりシンプルな感じで書きました。

ウォーミングアップ問題2

問題

下記のファイルを2列目の数字の小さい順に並べ替えてください。

$ cat list

b 11
d 5
a 3
e 4
c 2

解法

cat list | sort -k2,2 -n
cat list | awk '{s[$2]=$0}END{for (i in s){print s[i]}}'

解説

sortのオプションで二列目についてソートするようにしていすると簡単です。

awkでソートといえば連想配列ですが、今回は最初にふたつ目のキーの範囲がわかっていないという体裁で解きました。gawkだとこれで整列されるみたいですがmawkだと別の順になったのであんまり正当派解法ではないです。

ウォーミングアップ問題3

問題

文字列の並びを逆にしてください。

$ echo 'a b c'

解法

echo 'a b c' | rev
echo 'a b c' | awk '{for (i = NF;i>1;i--){printf "%s%s",$i,FS}print $1}'

解説

revというまさにこの用途のコマンドがあるので使いました。ちなみに行レベルでひっくり返したいときはtacを使います。

awkの方は普通に書きました。

ウォーミングアップ問題4

問題

下記の日付一覧から、月毎の日の数を数えてください

$ cat date1

20150101
20150121
20150201
20150202
20150203
20150310

解法

cat date1 | cut -c1-6 | uniq -c
cat date1 | awk '{s[substr($0,1,6)]++}END{for (i in s){print i,s[i];}}'

解説

cutで月のデータだけを切り出して uniq -c で行数を数えました。ソート済みでない場合はuniqの前に sort -n とかをしないといけないです。

awkの方も大体同じ感じで解きました。uniq相当のものは配列で作ります。

ウォーミングアップ問題5

問題

日付と商品が売れた数があります。月毎の商品の売れた数を数えてください。

$ cat date2

20150101 1
20150121 2
20150201 5
20150202 2
20150203 3
20150310 2

解法

cat date2 | cut -c 1-6,9-10 | xargs -I{} bash -c "yes \$(echo {}| cut -d ' ' -f 1) | head -n \$(echo {} | cut -d ' ' -f 2)" | uniq -c
cat date2 | awk '{s[substr($1,1,6)]+=$2}END{for (i in s){ print i,s[i];}}'

解説

awkを使わない方はかなり気合を入れて書きました。回数を数えるのにuniqを使おうとすると月ごとの行を商品の個数回繰り返す必要があります。それをyesとheadで各月について繰り返して、xargsで回しました。awkもtukubaiも使わなくてももう少し良い解法があるような気がします。

awkの方は普通に足し算をしました。

ウォーミングアップ問題6

問題

下記のファイルの積集合と和集合を出力してください。(註: ここで言う積集合とは直積(product)集合ではなく結び(intersection)の方です。)

$ cat data1

a
b
c
d

$ cat data2

b
d
e

解法

cat data1 | grep -f data2
cat data1 data2 | sort | uniq
cat data1 |awk '++s[$1];END{while((getline < "data2")>0){if(!s[$1])print;}}'
cat data1 | awk '{s[$1]++}END{while((getline < "data2")>0){if(s[$1])print $1;}}'

解説

awkを使わない方は大したことないです。 grep -f を知らないと辛いかもしれません。

awkの方はちょっと辛いです。awkで複数ファイルを扱うときには getline を使います。あとは大体前に書いたuniq相当の連想配列の使い方と同じ感じです。

ここから本題とは関係ない補足です。このgetlineの使い方はそんなに問題ないですが、BEGIN/END以外の場所で特にファイル指定をしないでgetlineを使ったりすると何を言っているのかわかりづらい挙動をするので、回避できるならやめたほうが良いとされています。

途中で力尽きたので第二回シェル芸勉強会の問題は後半ということにします。