jus & USP友の会共催 シェルワンライナー勉強会@関西(第11回シェル芸勉強会) に参加してきました。
USP の勉強会には初参加でしたが、楽しかったです。
今回の問題文は 20140614 【問題だけスライド】jus & USP友の会共催 シェルワンライナー勉強会@関西(第11回シェル芸勉強会) で公開されています。
準備
持ち物のところで「open usp Tukubai の入ったUNIX/Linux環境のあるノートPC(Macでも可)」とあったので、 https://github.com/usp-engineers-community/Open-usp-Tukubai をインストールした Linux 環境を用意したのと、 Tukubai on FreeBSDダウンロード から ova ファイルをダウンロードして VirtualBox にインポートして用意しておきました。
結局 open usp Tukubai は不要でしたが、 用意していた Linux 環境は動作確認に使いました。
ソフトウェアツールとAWK・sedについて座学
最初の「ソフトウェアツールとAWK・sedについて座学」の話からのメモです。
- フルスクラッチから1日でCMSを作る シェルスクリプト高速開発手法入門 という本が出ますという話
- IEEE から出ている Netizens という本からの話
- ed コマンド, grep コマンド
- パイプとフィルタコマンド
- sed, awk
- 現在の GNU coreutils に入っているようなコマンド群
- 実際にコマンドを実行してみる話
-
echo です。ます。でした。 | sed 's/。/&\n/g'
- OSX だと
echo です。ます。でした。 | sed 's/。/&\本当に改行/g'
でOK (これは Linux でも OK) - bash や zsh なら
sed $'s/。/&\\\n/g'
で本当の改行の代わりに$'\n'
でも大丈夫
- OSX だと
-
- 縦と横の話
echo {1..10} | awk '{for(i=1;i<=NF;i++){a+=$i};print a}'
echo {1..10} | tr ' ' '\n' | awk '{a+=$1}END{print a}'
- 複雑な例 sm2 というのは tukubai のコマンドだが今回はなくても良い
$ cat input
a 1
b 3
a 4
b 2
c 1
$ cat input | awk '{x[$1]+=$2}END{for (k in x){print k,x[k]}}'
a 5
b 5
c 1
$ cat input | sort | sm2 1 1 2 2
a 5
b 5
c 1
チーム分け
結構適当でした。
前半戦
1問目
echo -12,135,123 135,123
を足すという問題。
- 最初に試した方法
echo -12,135,123 135,123 | tr -d ',' | tr ' ' '+' | bc
- 話の流れでいくと
echo -12,135,123 135,123 | sed 's/,//g' | awk '{print $1+$2}'
- 出力にも
,
を入れるなら"%'d"
を使ってecho -12,135,123 135,123 | sed 's/,//g' | awk '{printf "%'"'"'d\n", $1+$2}'
-
,
を消すのも awk を使うならecho -12,135,123 135,123 | awk '{gsub(",","");printf "%'"'"'d\n", $1+$2}'
2問目
以下のデータの順番を「名前 点数」に統一するという問題。
$ cat score
45 鎌田
浜田 72
今泉 84
54 上田
62 斉藤
勘違いして点数の方を左にしてしまっていました。
cat score | awk '$1~/[0-9]/{print $2,$1} $2~/[0-9]/{print $1,$2}'
cat score | sed 's/\([^0-9 ]*\) \([0-9]*\)/\2 \1/'
twitter 上で出ていた解答に比べて全然ダメな感じでした。
3問目
m/s に変換する問題。 (1マイル=1609m)
$ cat speed
100km/h
16mph
cat speed | sed 's,km/h,*1000/3600,;s,mph,*1609/3600,' | bc | sed 's,$,m/s,'
cat speed | awk '/km/{print $1*1000/3600,"m/s"}/mph/{print $1*1609/3600,"m/s"}'
bc
だと整数になってしまうので、小数点以下の数値もほしければ awk
を使って計算する方が良かったようです。 Google 電卓で検算できるのが便利でした。
4問目
さいとうさん、さわださん、ひろたさん、いとうさんの数を数えてください。
$ cat name
齋藤 斉藤 沢田 澤田 伊藤
齋藤 齊藤 広田 廣田
最初は総数を数えればいいのかと思って egrep -o 'さいとう|さわだ|ひろた|いとう' | wc -l
というのを考えていたのですが、名前ごとにカウントだったようです。
全くと言って良い程リダイレクトが無くて cat ばっかりというツイートを受けて、頭に < input
を付ける方法に切り替えました。 cat
を使っているのは入力を先頭に書きたいからのはず、ということも合わせて、 あまり使われているのを見かけませんが、頭に付けるようにしました。
kakasi をインストールするのは面倒そうだったので、 sed
でがんばって < name sed 's/[齋斉齊]藤/さいとう/g;s/[沢澤]田/さわだ/g;s/[広廣]田/ひろた/g;s/伊藤/いとう/g' | egrep -o 'さいとう|さわだ|ひろた|いとう' | sort | uniq -c | sort -n
としました。
休憩時間中に Unicode の異体字データベースとかで同一視するためのデータがないか探してみたのですが、時間不足で見つけられませんでした。
後半戦
csv
csv の内容を全部足す問題。
$ cat csv
1,2,"123,456",-5,"-123,444"
6,7,8,"12",9
行ごととかいうこともなく、全部足せば良いという問題でした。
""
の処理はシェルに任せれば良いかと思ったら、 そういうことをするコマンドは xargs
だったので、 <csv tr ',' ' ' | xargs -n1 | tr -d ' ' | awk '{a+=$1}END{print a}'
という解答になりました。
matrix
行列の転置。
$ cat matrix
a b c
d e f
g h i
ファイルを複数回読むのはズルかと思って、 連想配列にためるようにして < matrix awk '{for(i=1;i<=NF;i++){m[i]=m[i]" "$i}}END{for(k in m){print m[k]}}'|sed 's/^ //'
となりました。 端の処理がうまく出来なかったので、sed
で後処理しました。 時間優先の時は、単独でうまく書けなくても、他の方法を組み合わせてなんとかする、と方法もありだと思います。
解答例では cat matrix | awk '{for(i=1;i<=NF;i++){print NR,i,$i}}' | sort -k2,2 | awk '{print $3} | xargs -n 3
ということで一度 行番号 桁番号 内容
という形式に変換するのがポイントと言っていました。
IPv6 その1
IPv6 アドレスの省略された 0
を復元する問題。
とりあえず時間内に解けることを優先して、 ` echo 2001:db8:20:3:1000:100:20:3 | xargs -d: -n1 | sed ‘s/^/0000/;s/.*(….)$/\1/’ | xargs | tr ‘ ‘ : | sed -e ‘s/:0000$//’` となりました。
後で確認したら xargs -d:
は Mac OS X の xargs
だと使えませんでした。 さらに xargs -d: -n1
だと余計な改行が付くので、最後に sed
で削除しているのですが、 tr : ' ' | xargs -n1
にすればそもそも余計な改行が付かなかったようです。
それから、他の解答例をみると、前につめるのは 0000
じゃなくて 000
で十分でした。
IPv6 その2
時間内に解くために思いついた方法をどんどん試して <ipv6 awk -F: '{n=8-NF;for(i=0;i<=n;i++){sub("::",":0::")}sub("::",":")}1' | sed 's/:/:000/g;s/:[^:]*\([^:][^:][^:][^:]\)/:\1/g'
となりました。
内容を書き換えると NF
が壊れてしまうので i<=8-NF
だとうまくいきませんでした。 そこで一度 n
に保存してから for
ループで 0
をつめていきました。 つめる場所を保存するために ::
は残しておいて、後で :
に置き換えています。
1
は Anarchy Golf 関連で知っていた ‘{print}’ の短縮のようなものです。
その1の時の方法は途中で複数行に分割していて、複数行の IPv6 アドレスを同時に扱うのには使えなかったので、 sed 's/:/:000/g;s/:[^:]*\([^:][^:][^:][^:]\)/:\1/g'
にかわりました。 後ろ4文字を残すというのは、素直に [^:]
を列挙しても twitter に投稿できる文字数に収まりました。
今回の問題だけなら良いのですが、先頭が 2001
で既に4文字になっているのを利用して、処理を省略しているので、汎用的にするなら、そこも処理する必要があります。
ruby なら ruby -r ipaddr -nle 'puts IPAddr.new($_).to_string' ipv6
で出来ました。
感想
twitter のハッシュタグ (#シェル芸
) での他の人の解答を参考にしたり、自分の解答を紹介したりできたり、頭リダイレクトが流行ったりして面白かったです。