Tambourine作業メモ

主にスキル習得のためにやった作業のメモ。他人には基本的に無用のものです。

テキスト行を内容でソートする

こんなテキストがある。最初のフィールドでソートしたい。ワンライナーで行きたいところだ。

(実際はduの出力を分析していた)

$ cat hoge.txt
20: rrr
1: aaa
3: ccc
11: jjj
10: iii
2: bbb

もちろん、この場合はsortコマンドではダメだ

$ cat hoge.txt |sort
1: aaa
10: iii
11: jjj
2: bbb
20: rrr
3: ccc

これは意図通りではない。最初のフィールドを数字だと理解してソートしたい。

Rubyだとこんな感じになる。

$ cat hoge.txt |ruby -ne 'BEGIN{a=[]}; a.push([$1.to_i, $_]) if /(\d+):/
> END{a.sort{|i,j| i[0] <=> j[0]}.each{|i| print i[1]}}'
1: aaa
2: bbb
3: ccc
10: iii
11: jjj
20: rrr

長いから折り返した・・・らワンライナが台無し。結構複雑だ。BEGINとENDはワンライナでは便利なので覚えておきたい

同じ事をPowerShellでやる

PS T:\PowerShell> cat hoge.txt |%{if($_ -match "(\d+):"){add-member -in $_ noteproperty i ([int]$matches[1]) -passthru}else{$_}} |sort i
1: aaa
2: bbb
3: ccc
10: iii
11: jjj
20: rrr

PowerShellの場合、そもそも入力がコレクションとしてやってくるので、Rubyでいうa=[]の処理が要らない。その代わりに、コレクションの中身のオブジェクト一つずつに最初のフィールドの内容を持たせてやることになる。

最初にcat hoge.txt(実態はGet-Content hoge.txt)を実行するとパイプラインには文字列型のオブジェクトが流れてくる。

Add-Memberは、-InputObjectで指定するオブジェクトにプロパティやらメソッドやらを追加することができる。

指定のオブジェクトはPSObjectでラップされている必要があるので、そうでない型(例えば文字列)の場合には、-passthruオプションを付けて新しいオブジェクトを生成して、代入しなくてはいけない・・・と「PowerShellインアクション」に書いてある。今回は後続へオブジェクトを受け渡すので-passthruを付けておけばいいだけである。後は、そのプロパティでソートすればいい。

しかし、このAdd-Memberは動きがかなりミラク

PS T:\PowerShell> $hoge = "hoge"
PS T:\PowerShell> Add-Member -InputObject $hoge noteproperty piyo "piyo"
PS T:\PowerShell> $hoge.piyo
PS T:\PowerShell> Add-Member -InputObject $hoge noteproperty piyo "piyo"
PS T:\PowerShell> $hoge.piyo
piyo

なぜ2回目のAdd-Memberではメンバー追加が出来たのか。いろいろ試した上での推測だが、3行目の$hoge.piyoを実行した際にRubyで言うところのmethod-missingが走り、$hogeをPSObjectでラップしてしまったようだ。以下のように単純に2回実行しても、piyoプロパティにはアクセスできない。

PS T:\PowerShell> $hoge = "hoge"
PS T:\PowerShell> Add-Member -InputObject $hoge noteproperty piyo "piyo"
PS T:\PowerShell> Add-Member -InputObject $hoge noteproperty piyo "piyo"
PS T:\PowerShell> $hoge.piyo
PS T:\PowerShell>

なにかメンバに触っておくと追加できる

PS T:\PowerShell> $hoge = "hoge"
PS T:\PowerShell> $hoge.length
4
PS T:\PowerShell> Add-Member -InputObject $hoge noteproperty piyo "piyo"
PS T:\PowerShell> $hoge.piyo
piyo

非常に気持ち悪い・・・