先日実施された、第41回シェル芸勉強会に参加してきたので、その復習。(最近はツールの作成の方ばっかやってて、ブログ 書くの久しぶりだなぁ…(´・ω・`)) 4月開催というのもあってか、いつものに比べたらマイルド?らしい。(確かにまぁ、そう言われるとそうかも…?) 今回はsortの処理に主眼を置いたとのこと。

問題は こちら 。最初に、問題等に使用するファイルをgitからcloneしておくといい。 今回はsortが主題ということもあってか、結構ファイルを使うようだ。

git clone https://github.com/ryuichiueda/ShellGeiData

Q1

リポジトリに入っている「excel」というテキストデータの2列目がExcelの列形式(1列目から順にアルファベットがA,B,Cとならんで、Zまできたら桁上りする)になっているので、それをExcelの列の並びにしてsortするという問題。

最初、 前にやってたExcelの列名対応一覧 とかいうのを元にできないかと試してたのだが、ちょっと無理があったようだ。 とりあえず、時間内に終わらせるために、他の人もやってたけど桁数の列を追加して、そこを1つ目のキーにしてsortさせるという処理で対応した。

cat excel|awk '{print $0,length($2)}'|sort -k3n -k2|cut -d\  -f1-2
[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < cat excel # excelファイルの中身を見る
114514 B
593195 AA
1192296 CEZ
4120 TZ
999 QQQ

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < cat excel | awk '{print $0,length($2)}' # 出力結果の後ろに、2列目の桁数を追加する
114514 B 1
593195 AA 2
1192296 CEZ 3
4120 TZ 2
999 QQQ 3

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < cat excel | awk '{print $0,length($2)}' | sort -k3n -k2 # sortで、3列目→2列目の優先度で並べ替えをする
114514 B 1
593195 AA 2
4120 TZ 2
1192296 CEZ 3
999 QQQ 3

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < cat excel | awk '{print $0,length($2)}' | sort -k3n -k2 | cut -d\  -f1-2 # cutで1、2列目だけを表示させる
114514 B
593195 AA
4120 TZ
1192296 CEZ
999 QQQ

Q2

干支が書かれているファイル「eto_yomi」を、干支順に出力させるという内容。 補助のために「eto」というファイルも用意されているので、それらを利用する。

こういう場合、やはりjoinでファイル同士を結合してやるというのが王道のような気がする。

join -o 2.2 2.3 1.1 -j 2 <(grep -o . eto|cat -n|sort -k2) <(cat -n eto_yomi|sort -k2) | sort -k3n | cut -d \  -f1-2
[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < grep -o . eto|cat -n|sort -k2 # A. 行番号を付与して干支を辞書sortして出力
    12  亥
     4  卯
     2  丑
     7  午
     1  子
     9  申
     5  辰
     3  寅
    10  酉
     8  未
     6  巳
    11  戌

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < cat -n eto_yomi|sort -k2 # B. eto_yomiも行番号を付与して干支順に辞書sort
    10  亥 い
     4  卯 う
     7  丑 うし
    11  午 うま
     2  子 ね
     1  申 さる
     6  辰 たつ
     3  寅 とら
     8  酉 とり
    12  未 ひつじ
     5  巳 み
     9  戌 いぬ

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < join -o 2.2 2.3 1.1 -j 2 <(grep -o . eto|cat -n|sort -k2) <(cat -n eto_yomi|sort -k2) # AとBの出力をjoinで結合
亥 い 12
卯 う 4
丑 うし 2
午 うま 7
子 ね 1
申 さる 9
辰 たつ 5
寅 とら 3
酉 とり 10
未 ひつじ 8
巳 み 6
戌 いぬ 11

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < join -o 2.2 2.3 1.1 -j 2 <(grep -o . eto|cat -n|sort -k2) <(cat -n eto_yomi|sort -k2) | sort -k3n | cut -d \  -f1-2 # 3列目を基準にsortして列を削除
子 ね
丑 うし
寅 とら
卯 う
辰 たつ
巳 み
午 うま
未 ひつじ
申 さる
酉 とり
戌 いぬ
亥 い

その他、もうちょっと短いやり方として、grepとsed(からまたgrepを実行)というやり方がある。 GNU sedのeコマンドを利用することで、置換結果をそのままコマンドとして実行させられるので、使える環境だと結構便利だったりする。

grep -o . eto | sed 's/./grep & eto_yomi/ge'
[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < grep -o . eto | sed 's/./grep & eto_yomi/ge' # grepでeto順にeto_yomiを読み込む
子 ね
丑 うし
寅 とら
卯 う
辰 たつ
巳 み
午 うま
未 ひつじ
申 さる
酉 とり
戌 いぬ
亥 い

Q3

ファイル「kim_calc」の内容を、計算式の数字が少ない順にsortするという問題。

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < cat kim_calc
1+2+4 金正日
4*3 金正男
3-1-5 金日成
495/3 金正恩
0x1F 金正哲

こういった計算式の場合、とりあえずbashの算術展開につっこめばよしなにしてくれるので、それで対応する。

sed -r 's/^([^ ]+)/echo $((\1)) \1/ge' kim_calc | sort -k1n | cut -d' ' -f2-
[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < sed -r 's/^([^ ]+)/echo $((\1)) \1/ge' kim_calc | sort -k1n
-3 3-1-5 金日成
7 1+2+4 金正日
12 4*3 金正男
31 0x1F 金正哲
165 495/3 金正恩

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < sed -r 's/^([^ ]+)/echo $((\1)) \1/ge' kim_calc | sort -k1n | cut -d' ' -f2-
3-1-5 金日成
1+2+4 金正日
4*3 金正男
0x1F 金正哲
495/3 金正恩

Q4

sjis」というSJISのファイルがあるので、これを辞書順(要は文字順)・数字順にsortするという内容のようだ(最初、いまいちよくわかってなかった…)。

Q4.1. 辞書順

こちらについては単純に、sjisファイルをnkfでutf-8にしてsort、そしてそのままSJISに戻してやればいい。

nkf -w sjis | sort | nkf -s

Q4.2. 数字順

こちらについては、一度全角文字になっているのを半角にしてやる必要がある。 nkfでは-Zオプションを付与することで全角→半角に変換することができるのだが、その逆についてはできない。このため、半角→全角については uconv を利用する。

nkf -wZ sjis | sort -n | uconv -x Halfwidth-Fullwidth | sed 's/ / /g' | nkf -s

Q5

size」という、KBやらMBの数字が書かれているファイルがあるので、それをサイズ順にsortするという内容。 こういう場合はnumfmtで一度戻してやって、そのままsort -hで処理するのが楽そうだ。

paste <(sed 's/B//g;s/k/K/g' size|numfmt --from=si) size | sort -k1n | cut -f2-
[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < paste <(sed 's/B//g;s/k/K/g' size|numfmt --from=si) size | sort -k1n | cut -f2-
0.4GB
410MB
1.2GB
2GB
40000MB
1000000000kB

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < sed 's/B//g;s/k/K/g' size # numfmtに渡せるように整形する
2G
1.2G
40000M
1000000000K
0.4G
410M

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < sed 's/B//g;s/k/K/g' size|numfmt --from=si # numfmtでバイトに変換
2000000000
1200000000
40000000000
1000000000000
400000000
410000000

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < paste <(sed 's/B//g;s/k/K/g' size|numfmt --from=si) size # pasteで結合
2000000000  2GB
1200000000  1.2GB
40000000000 40000MB
1000000000000   1000000000kB
400000000   0.4GB
410000000   410MB

[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < paste <(sed 's/B//g;s/k/K/g' size|numfmt --from=si) size | sort -k1n | cut -f2- # sortして1列目削除
0.4GB
410MB
1.2GB
2GB
40000MB
1000000000kB

numfmtはCentOS7くらいの頃からcoreutilsに入ってきてるコマンドなので、大体のLinuxで使えると思う。

Q6

nums」というファイルがあるので、それをbashの内部コマンドとsleepを使って処理しよう、という問題。 内部コマンドのみとなっているのでcatも使えない状態。なので、while等を使って無理やりファイルを読み込ませるといったことも必要になる。

とりあえず、以下のようにすれば対応できた。

while read a;do (sleep $a && echo $a ) & done < nums

 

Q7

roman」という、ローマ数字の書かれたファイルがあるので、これをsortするという内容。 で、自前で処理するのはめんどくさいのでなんか無いかなと調べてみたところ、どうやら numconv というローマ数字や漢数字等を変換するコマンドがあるらしい(知らなかった…)。

Ubuntuであれば、aptから以下のコマンドでインストールができる(yumやpacman、brewではインストールできない様子)。

sudo apt install numconv

numconvを使えば、以下で処理が完了する。

paste <(cat roman|numconv) roman | sort -k1n | cut -f2
[REMOTE][blacknon@bs-pub-ubuntu-03][~/ShellGeiData/vol.41]
(`・ω・´) < paste <(cat roman|numconv) roman | sort -k1n | cut -f2
IV
VIII
IX
XI
XX
XLIII
LXXXIX

Q8

gagigugego」という濁点付きのファイルがあるので、これを濁点付きが先にくるようにsortさせるという内容。 通常は濁点付きは後になるので、これをどう処理するかという内容らしい。

で、自分は午前の部に出てなかったのだが、どうやら一度uconvで変換してやることでこの処理ができるらしい(知らなかった…)。

uconvにこんな使い方があるとは知らなかった…。

numfmtはよく使ってたけど、numconvは知らなかったな。 どうやら漢数字等も扱えるようなので、ちゃんと試してみたいところ。yumやpacmanで扱えないのはちょっと辛いので、なんかインストール方法調べたいかなと。