SNSへはこちら

第29回激しいシェル芸勉強会

電子工作のみならず、プログラミング系もやりますよー
その中でも一風変わったのが、今回やるシェル芸です。皆さんはターミナル(端末)使いますか??え?テキストエディタとgccにしか使ってない??
大半の方がそうなのでしょうが、このターミナルのコマンドを駆使することで圧倒的成長!!!!失礼しました。圧倒的にデータ処理の効率化が図れるのです。(以下シェルコマンドと呼びます。コマンドを打ってコンパイル実行のためのソフトウェアを呼び出したりするプログラムをシェルと言います。)
んで、シェル芸ってなんぞや?って人は下の上田さんのブログで定義を確認してください。
シェル芸 – 上田ブログ

こんな感じで、所謂プログラミングとは一線を画すわけです。ソースコードは残さないわ、インストールされているプログラムに依存するわと言った感じです。つまりですね、一般性や汎用性はさておき、便利であれば何やってもいいじゃん!と言うわけです。非常に分かりやすい理念。

・・・とまあ前置きが長くなりましたね。こう言うのオッサンっぽいなぁとよく思います。まだ22歳だと言うのに...さて、シェル芸の基本等はググれいろんなサイトでやってると思いますので是非調べてトライしてみてください。

と言うわけで、今回の問題をチマチマ解いてますので、出来次第以下に投下していきます。解答と、他の変態お方から得られた知見も併記しています。問題はこちら↓

【問題のみ】第29回激しいシェル芸勉強会 – 上田ブログ

Q1

$ cat kadai{1,2} | awk '{name[$1]=$2;val[$1]+=$3;} END{for(i in name){print i, name[i], val[i]}}' | sort

awk天国。最近すぐこのコマンドに逃げてしまいます。まぁ便利だからいいか。

$ join -j1 -a{1,2} kadai{1,2} | awk '{print $1, $2, $3+$5}'

joinコマンドを使ってみました。これは2つのファイルからデータを貰って指定フィールドが一致したら行を結合するというもの。お察しの通りawkと親和性がよく、またコマンドサイズを小さくしてくれ分かりやすくなります。

$ cat kadai1 kadai2 | datamash sum 3 -g1 -W -s -f | awk '{$3="";print}'

勉強会でも話題になった(まだライブストリーミングの録画を見ていませんが)datamashコマンドです。まだそんなに触れていないのでよく分かりませんが、ポテンシャルが大きいと思います。個人的には皆が待ち望んでいたであろう神コマンドだと思います。基本的に縦方向に計算をしていくようで、グルーピングごとに結果を出せるので重宝しそうです。-fで全文と計算結果を出力できるのですが、指定フィールドを削除する機能がないのでこちらもcutawkと併用したほうが良さそうです。

datamash --help によると...

Primary operations:
  groupby, crosstab, transpose, reverse, check
Line-Filtering operations:
  rmdup
Per-Line operations:
  base64, debase64, md5, sha1, sha256, sha512,
  bin, strbin, round, floor, ceil, trunc, frac
Numeric Grouping operations:
  sum, min, max, absmin, absmax
Textual/Numeric Grouping operations:
  count, first, last, rand, unique, collapse, countunique
Statistical Grouping operations:
  mean, median, q1, q3, iqr, mode, antimode, pstdev, sstdev, pvar,
  svar, mad, madraw, pskew, sskew, pkurt, skurt, dpo, jarque,
  scov, pcov, spearson, ppearson

らしいです(一部抜粋)。

Q2

$ cat <(cat attend6 | sed 's/,/ \n/g' | sort) attend | awk -v 'cnt=1' 'NF==1{a[NR]=$1;next} {if($1==a[cnt]){print $0 "出";cnt++}else print}'

出〜出〜〜wwwと言うわけでまたまたawk。もっとエレガントな解答が欲しいところ。

$ cat attend6 | sed -E 's/(,|$)/ ,\n/g;' | sort | LC_ALL=C join -j1 -a{1,2} attend - | awk '$0&&!$4{$3=$3 "欠"} $4{$4="";$3=$3 "出"} $0'

長い!がjoinを使った例。結局awkに頼っているんですけどね。

Q3

$ LC_ALL=C join -j1 -a1 attend test | awk '$3 !~ /(出欠*){3,}/||NF!=4{$0=$1" "$2" "$3" 0"} {print}'

join の底力を見た。このコマンドはいちいちLC_ALL=Cとしなければならない点が面倒。だが割りといい感じに出来た。

Q4

Q4-1

$ echo -1 4 5 2 42 421 44 311 -9 -11 | grep -o '[-0-9]*' | sort -n | awk  'NR==1{printf $0 " ";a=length;next} {if(a!=length){printf "\n" $0 " "}else{printf$0" "};a=length;next} END{print""}'

はいはい、awk awkですよ〜〜〜〜

Q4-2

未完です。(

Q5

$ cat triangle | sed 's/ /* /g' | rs -T | tac | rev | tac | tr -dc '\n[0-9a]' | awk '{for(i=1;i<=4-NR;i++)printf " "; print}' | sed 's/[0-9a]/& /g'

ピラミッドを無理やり四角にしてぐるぐるしました。

Q6

$ cat prime | sed ' s/ / aa\n/g; $s/$/ aa/' | join -j1 - -a2 <(seq 100 | factor | awk 'NF==2&&$0=$2 " bb"') | awk '$3{printf$1 " "} !$3{print $3}' | awk '!/^$/'

いろいろゴチャってますが、いちおう100までの素数表をseq 100 | factor | awk 'NF==2&&$0=$2 " bb"'で取得し、それを比較しています。最後のawk '!/^$/'では空行を削除。

Q7

$ cat nyaan.html | xmllint --encode utf-8 - | awk '/^<span style="">/' | sed 's/<[^>]*>//g'

非常にエレガント。xmllint に文字参照を変換する機能があったんですねぇと。

Q8

$ cat shellgei | sed 's/./&* /g' | rs -T | sed '/^[ \*]*$/d' | rs -T | sed 's/\*  //g;s/\*$//'

rsコマンドの真骨頂を見た。こういうパズル大好きです。rs -Tはスペースを区切り文字として、転置を行うコマンド・オプションとなります。シェル芸における重要コマンドの1つですね。本問の場合はスペース自体も1つの要素ですので、スペース自体をアスタリスクと区切り文字としてのスペースに置換することで1つの要素としています。

まとめ

今回のシェル芸問題、「激しい」と名前ほど難しくはないかな?と思ったものの、いざやってみるとたしかに骨のある問題ばかり。静かながらも激しさを感じることがありました。しばらくここまでガチなシェル芸に触れていなかったのですぐawkに頼ったりとシェル芸力の低下をひしひしと感じました。とりあえずなにかいいワンライナーを考えたらこっそりと更新しようと思います。