我有一個包含一些 IP 的純文本檔案,如下所示:
194.225.0.0 - 194.225.15.255
194.225.24.0 - 194.225.31.255
62.193.0.0 - 62.193.31.255
195.146.53.128 - 195.146.53.225
217.218.0.0 - 217.219.255.255
195.146.40.0 - 195.146.40.255
85.185.240.128 - 85.185.240.159
78.39.194.0 - 78.39.194.255
78.39.193.192 - 78.39.193.207
我想按 IP 地址對檔案進行排序。我的意思是只有第一部分很重要。
我用谷歌搜索并找到了一些程式,但我想知道這是否可以通過 Powershell 而沒有其他應用程式。
我有這樣的 Linux 方式,但無法在 Windows 中訪問它:
sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 file
更新1
@TheMadTechnician,這是我運行您的命令時的輸出:
85.185.240.128 - 85.185.240.159
195.146.40.0 - 195.146.40.255
78.39.193.192 - 78.39.193.207
78.39.194.0 - 78.39.194.255
217.218.0.0 - 217.219.255.255
194.225.24.0 - 194.225.31.255
194.225.0.0 - 194.225.15.255
195.146.53.128 - 195.146.53.225
62.193.0.0 - 62.193.31.255
uj5u.com熱心網友回復:
一個使用 RegEx-replace 的簡單解決方案:為了使 IP 地址可排序,我們只需在左側填充每個八位位元組,使它們都具有相同的寬度。然后一個簡單的字串比較會產生正確的結果。
對于 PS 6 :
Get-Content IpList.txt | Sort-Object {
$_ -replace '\d ', { $_.Value.PadLeft(3, '0') }
}
對于 PS 5.x:
Get-Content IpList.txt | Sort-Object {
[regex]::Replace( $_, '\d ', { $args.Value.PadLeft(3, '0') } )
}
-replace運算子嘗試在給定字串中查找正則運算式模式的匹配項,并用給定值替換它們。- 對于PS 5.x,我們需要不同的語法,因為
-replace不支持scriptblock。使用 .NETRegex.Replace方法我們可以實作相同的目的。 - 第一個
$_表示文本檔案的當前行。 \d是匹配每個 IP 地址的每個八位位元組的模式。有關詳細說明,請參見regex101中的示例。{}定義一個輸出替換值 的腳本塊- 這里
$_表示當前匹配(八位位元組)。我們取它的值并在左邊用零填充它,所以每個八位位元組總共是 3 個字符(例如2become002和92become092)。最終 IP 可能看起來像194.225.024.000.
- 這里
Tuple使用該類的另一種解決方案。它稍長一些,但更簡潔,因為它實際上比較的是數字而不是字串。
Get-Content IpList.txt | Sort-Object {
# Extract the first 4 numbers from the current line
[int[]] $octets = [regex]::Matches( $_, '\d ' )[ 0..3 ].Value
# Create and output a tuple that consists of the numbers
[Tuple]::Create( $octets[0], $octets[1], $octets[2], $octets[3] )
}
使用
[regex]::Matches()我們找到當前行的所有數字。從回傳的MatchCollection我們獲取前四個元素。然后我們使用成員訪問列舉來創建Value每個MatchCollection元素的成員的字串陣列。通過簡單地將字串陣列分配給具有
[int[]]型別約束(ints 陣列)的變數,PowerShell 會自動將字串決議為整數。排序之所以有效,是因為
Tuple實作了IComparable介面,該介面Sort-Object在可用時使用。元組按字典順序排序,正如預期的那樣。使用動態方法呼叫,我們可以像這樣縮短
[Tuple]::Create呼叫(最多可用于 8 個元素1):[Tuple]::Create.Invoke( [object[]] $octets )請注意轉換為
[object[]],否則[Tuple]::Create將只使用一個引數呼叫,即$octets陣列。
[1] 實際上大于 8 個元素的元組可以通過創建嵌套元組來創建(為剩余元素創建一個元組并將其存盤在基本元組的最后一個元素中)。一般來說,要做到這一點,它需要遞回或反向回圈,首先創建大多數嵌套元組。
uj5u.com熱心網友回復:
這個答案最初是作為我在另一個答案中的評論發布的
您可以將IP地址從string物件轉換為version物件,“巧合”與IP地址具有相同的格式(由a分隔的4組數字.)
Get-Content .\abc.txt | Sort-Object { [System.Version]($_).split("-")[1] }
uj5u.com熱心網友回復:
只要范圍起始地址在第一個八位位元組中不同,MadTechnician 的答案就有效。為了讓它按多個八位位元組排序,它看起來Sort-Object不會按單個回傳的陣列中的連續值排序[ScriptBlock];為此,您需要[ScriptBlock]為每個八位位元組傳遞一個。 Santiago Squarzon 的回答展示了如何在不重復定義四個幾乎相同[ScriptBlock]的 s 的情況下做到這一點。
相反,一個單一的[ScriptBlock]可以將每個八位位元組組合成一個[UInt32]進行排序的。
用于[Math]::Pow()產生可排序的值
Get-Content -Path 'IPv4AddressRanges.txt' |
Sort-Object -Property {
# Split each line on a hyphen surrounded by optional whitespace
$rangeStartAddress = ($_ -split '\s*-\s*')[0]
# Split the start address on a period and parse the resulting [String]s to [Byte]s
[Byte[]] $octets = $rangeStartAddress -split '.', 0, 'SimpleMatch'
#TODO: Handle $octets.Length -ne 4
# Alternative: [Byte[]] $octets = [IPAddress]::Parse($rangeStartAddress).GetAddressBytes()
[UInt32] $sortValue = 0
# $sortValue = (256 ^ 3) * $octets[0] (256 ^ 2) * $octets[1] 256 * $octets[2] $octets[3]
for ($i = 0; $i -lt $octets.Length; $i )
{
$octetScale = [Math]::Pow(256, $octets.Length - $i - 1)
$sortValue = $octetScale * $octets[$i]
}
return $sortValue
}
...輸出...
62.193.0.0 - 62.193.31.255
78.39.193.192 - 78.39.193.207
78.39.194.0 - 78.39.194.255
85.185.240.128 - 85.185.240.159
194.225.0.0 - 194.225.15.255
194.225.24.0 - 194.225.31.255
195.146.40.0 - 195.146.40.255
195.146.53.128 - 195.146.53.225
217.218.0.0 - 217.219.255.255
為了更好地衡量,您可以將第一行更改為...
@('255.255.255.255', '0.0.0.0') (Get-Content -Path 'IPv4AddressRanges.txt') |
...并看到它正確排序而沒有排序值溢位。
用于[BitConverter]產生可排序的值
您可以通過使用[BitConverter]該類將 IP 地址位元組直接轉換為[UInt32]...
Get-Content -Path 'IPv4AddressRanges.txt' |
Sort-Object -Property {
# Split each line on a hyphen surrounded by optional whitespace
$rangeStartAddress = ($_ -split '\s*-\s*')[0]
# Split the start address on a period and parse the resulting [String]s to [Byte]s
[Byte[]] $octets = $rangeStartAddress -split '.', 0, 'SimpleMatch'
#TODO: Handle $octets.Length -ne 4
# Alternative: [Byte[]] $octets = [IPAddress]::Parse($rangeStartAddress).GetAddressBytes()
# [IPAddress]::NetworkToHostOrder() doesn't have an overload for [UInt32]
if ([BitConverter]::IsLittleEndian)
{
[Array]::Reverse($octets)
}
return [BitConverter]::ToUInt32($octets, 0)
}
在 PowerShell 類中實作[IComparable]以定義自己的排序
更復雜的解決方案是將我們的地址存盤在實作[IComparable]介面的型別中,以便Sort-Object可以直接對地址進行排序,而無需指定[ScriptBlock]. [IPAddress]當然,它是存盤 IP 地址的最自然的 .NET 型別,但它不實作任何排序介面。相反,我們可以使用PowerShell 類來實作我們自己的可排序型別......
# Implement System.IComparable[Object] instead of System.IComparable[IPAddressRange]
# because PowerShell does not allow self-referential base type specifications.
# Sort-Object seems to only use the non-generic interface, anyways.
class IPAddressRange : Object, System.IComparable, System.IComparable[Object]
{
[IPAddress] $StartAddress
[IPAddress] $EndAddress
IPAddressRange([IPAddress] $startAddress, [IPAddress] $endAddress)
{
#TODO: Ensure $startAddress and $endAddress are non-$null
#TODO: Ensure the AddressFamily property of both $startAddress and
# $endAddress is [System.Net.Sockets.AddressFamily]::InterNetwork
#TODO: Handle $startAddress -gt $endAddress
$this.StartAddress = $startAddress
$this.EndAddress = $endAddress
}
[Int32] CompareTo([Object] $other)
{
if ($null -eq $other)
{
return 1
}
if ($other -isnot [IPAddressRange])
{
throw [System.ArgumentOutOfRangeException]::new(
'other',
"Comparison against type ""$($other.GetType().FullName)"" is not supported."
)
}
$result = [IPAddressRange]::CompareAddresses($this.StartAddress, $other.StartAddress)
if ($result -eq 0)
{
$result = [IPAddressRange]::CompareAddresses($this.EndAddress, $other.EndAddress)
}
return $result
}
hidden static [Int32] CompareAddresses([IPAddress] $x, [IPAddress] $y)
{
$xBytes = $x.GetAddressBytes()
$yBytes = $y.GetAddressBytes()
for ($i = 0; $i -lt 4; $i )
{
$result = $xBytes[$i].CompareTo($yBytes[$i])
if ($result -ne 0)
{
return $result
}
}
return 0
}
}
該[IPAddressRange]型別存盤范圍的開始地址和結束地址,因此它可以表示輸入檔案的整行。該CompareTo方法StartAddress逐位元組比較每個位元組,只有當它們相等時,它才會逐位元組比較每個EndAddress位元組。執行這個...
(
'127.0.0.101 - 127.0.0.199',
'127.0.0.200 - 127.0.0.200',
'127.0.0.100 - 127.0.0.200',
'127.0.0.100 - 127.0.0.101',
'127.0.0.199 - 127.0.0.200',
'127.0.0.100 - 127.0.0.199',
'127.0.0.100 - 127.0.0.100',
'127.0.0.101 - 127.0.0.200'
) (Get-Content -Path 'IPv4AddressRanges.txt') |
ForEach-Object -Process {
$startAddress, $endAddress = [IPAddress[]] ($_ -split '\s*-\s*')
return [IPAddressRange]::new($startAddress, $endAddress)
} |
Sort-Object
...按127.0.0.*預期順序對范圍進行排序...
StartAddress EndAddress
------------ ----------
62.193.0.0 62.193.31.255
78.39.193.192 78.39.193.207
78.39.194.0 78.39.194.255
85.185.240.128 85.185.240.159
127.0.0.100 127.0.0.100
127.0.0.100 127.0.0.101
127.0.0.100 127.0.0.199
127.0.0.100 127.0.0.200
127.0.0.101 127.0.0.199
127.0.0.101 127.0.0.200
127.0.0.199 127.0.0.200
127.0.0.200 127.0.0.200
194.225.0.0 194.225.15.255
194.225.24.0 194.225.31.255
195.146.40.0 195.146.40.255
195.146.53.128 195.146.53.225
217.218.0.0 217.219.255.255
請注意,我們只添加了對實體Sort-Object進行排序的功能,[IPAddressRange]而不是它的各個屬性。這些仍然是[IPAddress]不提供自己的排序的型別,所以如果我們嘗試類似的東西... | Sort-Object -Property 'EndAddress'不會產生預期的結果。
uj5u.com熱心網友回復:
一種簡單的方法是在 上分割每一行.,取第一部分(范圍內每個 IP 的第一個八位位元組),然后將其轉換為整數并對其進行排序。
Get-Content .\MyFile.txt | Sort-Object {$_.Split('.')[0] -as [int]}
uj5u.com熱心網友回復:
scottwang提供了一種巧妙的方法來對評論中的 IP 進行排序,使用實作Interface的VersionClass。IComparable
這是另一種選擇,顯然效率較低,使用哈希表、IPAddress類和運算式陣列:
$ips = Get-Content ipfile.txt
$iptable = @{}
foreach($line in $ips) {
if($ip = $line -replace ' -. ' -as [ipaddress]) {
$iptable[$line] = $ip.GetAddressBytes()
}
}
$expressions = foreach($i in 0..3) {
{ $iptable[$_] | Select-Object -Index $i }.GetNewClosure()
}
$ips | Sort-Object $expressions -Descending
使用高級函式或匿名函式可以在單個管道中執行相同操作:
Get-Content ipfile.txt | & {
begin {
$iptable = @{}
$expressions = foreach($i in 0..3) {
{ $iptable[$_] | Select-Object -Index $i }.GetNewClosure()
}
}
process {
if ($ip = $_ -replace ' -. ' -as [ipaddress]) {
$iptable[$_] = $ip.GetAddressBytes()
}
}
end { $iptable.PSBase.Keys | Sort-Object $expressions -Descending }
}
uj5u.com熱心網友回復:
我之前的回答效率很低,因此我決定使用實作介面并持有以下實體的類提供另一種選擇:IComparableIpAddress
class IpComparer : IComparable {
[ipaddress] $IpAddress
IpComparer ([string] $IpAddress) {
$this.IPAddress = $IpAddress
}
[int] GetHashCode() {
return $this.IpAddress.GetHashCode()
}
[string] ToString() {
return $this.IpAddress.ToString()
}
hidden static [bool] Equals ([object] $LHS, [object] $RHS) {
return $LHS.IpAddress -eq $RHS.IpAddress
}
[bool] Equals ([object] $Ip) {
return [IpComparer]::Equals([IpComparer] $this, [IpComparer] $Ip)
}
hidden static [int] CompareTo ([IpComparer] $LHS, [IpComparer] $RHS) {
$x = $LHS.IpAddress.GetAddressBytes()
$y = $RHS.IpAddress.GetAddressBytes()
for($i = 0; $i -lt 4; $i ) {
if($ne = $x[$i].CompareTo($y[$i])) {
return $ne
}
}
return 0
}
[int] CompareTo ([object] $Ip) {
if($null -eq $Ip) {
return 1
}
return [IpComparer]::CompareTo([IpComparer] $this, [IpComparer] $Ip)
}
}
現在實體可以是Comparable
[IpComparer] '194.225.0.0' -lt '194.225.15.255' # => True
[IpComparer] '194.225.15.255' -lt '194.225.0.0' # => False
[IpComparer] '194.225.0.0' -gt '194.225.15.255' # => False
[IpComparer] '194.225.15.255' -gt '194.225.0.0' # => True
平等測驗
[IpComparer] '194.225.15.25' -ge '194.225.15.25' # => True
'194.225.15.25' -le [IpComparer] '194.225.15.25' # => True
$hs = [Collections.Generic.HashSet[IpComparer]]::new()
$hs.Add('194.225.0.0') # => True
$hs.Add('194.225.0.0') # => False
([IpComparer[]]('194.225.0.0', '194.225.0.0') | Select-Object -Unique).Count # => 1
([IpComparer] '194.225.15.255').Equals('194.225.15.255') # => True
因此,Sortable:
Get-Content ipfile.txt | Sort-Object { $_ -replace ' -. ' -as [IpComparer] } -Descending
uj5u.com熱心網友回復:
可能是這樣的。
Get-Content .\abc.txt |ForEach-Object {($_).split("-")[1]}|Sort-Object
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/482212.html
標籤:电源外壳 排序 IP地址 powershell-5.1
