しばたテックブログ

気分で書いている技術ブログです。

PowerShellのSort-ObjectコマンドレットでIPアドレスのソートを行う

小ネタです。

orebibou.com

こちらの記事を見てPowerShellでもやってみました。

サンプルデータ

元記事と同じデータ(test.txt)を使います。

192.168.0.102
192.168.0.8
192.168.0.97
192.168.0.68
192.168.0.99
192.168.0.66
192.168.0.24

普通のソート結果はこんな感じです。
単純な文字列のソートになるので元記事同様に欲しい形にはなりません。

f:id:stknohg:20170215220431p:plain

version-sort

こちらは、私は最初思いつかなかったのですが、牟田口先生があっさり書いてくれました。流石です。

cat .\test.txt | sort {[Version]$_}

結果はこちら。

f:id:stknohg:20170215220506p:plain

解説

PowerShellのsort(Sort-Object)では-Propertyパラメーターでソートに使うプロパティを指定することができ、そのプロパティにスクリプトブロックによる式を指定することができます。

sort {[Version]$_}

の部分をより正確に書くと、

Sort-Object -Property { [Version]$_ }

となり、入力値の文字列$_(192.168.0.102など)をSystem.Versionクラスにキャストした結果でソートできる様になります。
これは、.NET Frameworkのバージョン指定が、[Major].[Minor].[Revision].[Build]とIPアドレスと同じ書式なため利用できる方法です。

各オクテットごとにソート

こちらは私が最初に思いついた方法です。
元記事同様に各オクテット順でソートします。

cat .\test.txt | sort (0..3|%{[ScriptBlock]::Create("[int]$`_.Split('.')[$_]")})

結果はこちら。

f:id:stknohg:20170215222853p:plain

解説

上記の

(0..3|%{[ScriptBlock]::Create("[int]$`_.Split('.')[$_]")})

の部分を展開すると、

{[int]$_.Split('.')[0]}, {[int]$_.Split('.')[1]}, {[int]$_.Split('.')[2]}, {[int]$_.Split('.')[3]}

となり、最終的なコマンドは

Sort-Object -Property ({[int]$_.Split('.')[0]}, {[int]$_.Split('.')[1]}, {[int]$_.Split('.')[2]}, {[int]$_.Split('.')[3]})

となります。
-Propertyでは入力値の文字列$_(192.168.0.102など)を.でスプリットした結果(各オクテット)を順にソート対象にしています。

2017/02/17追記 別解

元のデータが文字列なので、桁数が揃っていれば期待したソートをさせることが可能です。
なので別解として、

cat .\test.txt | sort { -join $_.Split('.').PadLeft(3)}

も可能です。

f:id:stknohg:20170217125211p:plain

この方法では入力文字列を、

192168  0102
192168  0  8
192168  0 97
192168  0 68
192168  0 99
192168  0 66
192168  0 24

の様に変換して桁数を揃えています。

最後に

とりあえずこんな感じです。
PowerShellのソートはかなり自由度が高く、何でもありな感じがしますね。