我正在尝试将此python模块移植到powershell
def str_to_tuple(s):
''' @source kindall at stackoverflow
http://stackoverflow.com/a/11887825/3447669
'''
return tuple(map(int, (s.split("."))))
最终结果是,我将python代码移植到powershell上,以便在Windows服务器上使用,因为其他团队不知道python。
我有一个python脚本,可以比较2个应用的版本号(即1.2.33和1.2.34)。上面的模块为我提供了一个元组,我可以在2个版本号>或<之间进行比较。
我正在专门测试运行时提供的
$appVersion -lt $myVersionString
,因此-eq
对我们的脚本来说还不够。
我尝试使用$str.split()
将字符串元素放入数组中,但是比较不起作用,并且我不想遍历每个数组来比较每个项目,因为版本号可能不同长度...
Examples of lengths
1.2
2.3.4
2.3.44
非常感谢任何指导。
答案 0 :(得分:2)
正如@Lee_Dailey指出的那样,最简单的解决方案是将字符串转换为类型System.Version
:
PS /home/me> $s1 = '1.2.3' PS /home/me> $s2 = '1.19.2' PS /home/me> [Version]$v1 = $s1 PS /home/me> [Version]$v2 = $s2 PS /home/me> $v1 Major Minor Build Revision ----- ----- ----- -------- 1 2 3 -1 PS /home/me> $v2 Major Minor Build Revision ----- ----- ----- -------- 1 19 2 -1 PS /home/me> $v1 -gt $v2 False PS /home/me> $v2 -gt $v1 True
我刚刚在Linux上的PowerShell Core 6.1中对此进行了测试,并且效果很好,因此我希望它也可以在Mac OS X上的PowerShell Core中运行。
但是,您也可以使用与Python中使用的方法完全相同的方法:从字符串创建tuples。
PS /home/me> $s1 = '1.2.3' PS /home/me> $s2 = '1.19.2' PS /home/me> [int[]]$v1 = $s1.Split('.') PS /home/me> [int[]]$v2 = $s2.Split('.') PS /home/me> $t1 = New-Object 'Tuple[int,int,int]' $a1 PS /home/me> $t2 = New-Object 'Tuple[int,int,int]' $a2 PS /home/me> $t1 Item1 Item2 Item3 Length ----- ----- ----- ------ 1 2 3 3 PS /home/me> $t2 Item1 Item2 Item3 Length ----- ----- ----- ------ 1 19 2 3 PS /home/me> $t1 -gt $t2 False PS /home/me> $t2 -gt $t1 True
请注意,分割字符串将为您提供一个字符串数组,因此您必须将其强制转换为整数数组,否则比较将无法正常工作(因为将使用字符串比较而不是数字比较)。
还请注意,类型定义(Tuple[<type>,<type>,...]
)中的元素数必须与创建元组的数组中的元素数相匹配。 This answer显示了一个可重用的函数,用于从任意数组创建元组:
function New-Tuple {
Param(
[Parameter(
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[ValidateCount(2,20)]
[array]$Values
)
Process {
$types = ($Values | ForEach-Object { $_.GetType().Name }) -join ','
New-Object "Tuple[$types]" $Values
}
}
所以您可以更改
$t1 = New-Object 'Tuple[int,int,int]' $a1
$t2 = New-Object 'Tuple[int,int,int]' $a2
到
$t1 = New-Tuple $a1
$t2 = New-Tuple $a2
但是请注意,比较元组需要它们具有相同数量的元素,否则比较将失败:
PS /home/me> $s3 = '1.19.2.1' PS /home/me> [int[]]$a3 = $s3.Split('.') PS /home/me> $t3 = New-Object 'Tuple[int,int,int,int]' $a3 PS /home/me> $t3 -gt $t2 Could not compare "(1, 19, 2, 1)" to "(1, 19, 2)". Error: "Cannot convert the "(1, 19, 2)" value of type "System.Tuple`3[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]" to type "System.Tuple`4[System.Int32,System.Int32,System.Int32,System.Int32]"." At line:1 char:1 + $t3 -gt $t2 + ~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : ComparisonFailure
因此,您必须确保版本元组始终具有相同的长度,例如通过将0
元素附加到数组,然后从结果中选择前4个元素:
[int[]]$a = ('1.2'.Split('.') + (0, 0, 0, 0))[0..3]
$t = New-Object 'Tuple[int,int,int,int]' $a
[Version]
类型的加速器没有此问题,因为它创建了相同类型的对象,其中版本字符串中的缺失数字会自动填充值-1
。
PS /home/me> [Version]'1.2' Major Minor Build Revision ----- ----- ----- -------- 1 2 -1 -1 PS /home/me> [Version]'1.2.3' Major Minor Build Revision ----- ----- ----- -------- 1 2 3 -1 PS /home/me> [Version]'1.2.3.4' Major Minor Build Revision ----- ----- ----- -------- 1 2 3 4
答案 1 :(得分:1)
Noticed Donald.M和Lee_Dailey已使用 PoshSematicVersion 与 [version]来解决注释中的解决方案。 (发现与PowerShell Core不兼容)
这是我在发布解决方案之前所研究的解决方案,也许可以与PowerShell Core一起使用-尽管不确定且未经测试。如果您不想要,这是我的解决方案。
但是,str_to_tuple()函数是一个线性函数,非常类似于原始函数!从技术上讲,不是返回元组而是一个 [System.Array] ,但是可能存在类型元组。
(注意:带有负版本控制的未知行为-甚至存在。)
function str_to_tuple($s) {
return (($s.split(".") | ForEach-Object {([int]::parse($_))}))
}
# returns 1 if left ($lhs) is greater than right
# returns -1 if right ($rhs) is greater than left
# returns 0 if both right and left versions equal
function comp_vers {
Param([Parameter(Position=0,Mandatory=$true)]$lhs, [Parameter(Position=1,Mandatory=$true)]$rhs)
$min = [math]::Min($lhs.count,$rhs.count)
for($i=0; $i -lt $min; $i++) {
if ($lhs[$i] -eq $rhs[$i]) { continue }
if ($lhs[$i] -gt $rhs[$i]) { return 1 }
else { return -1 }
}
if ($lhs.count -eq $rhs.count) { return 0 }
# Section 2 - compares version numbers further (for example, 1.1 versus 1.1.0.0 - should be equal)
$max = [math]::Max($lhs.count,$rhs.count)
$is_lhs_high = ($l.count -eq $max)
for($i = $min; $i -lt $max; $i++) {
if ($is_lhs_high) {
if ($lhs[$i] -gt 0) { return 1 }
} else {
if ($rhs[$i] -gt 0) { return -1 }
}
}
return 0
}
function vers_output($comp) {
Switch($comp) {
1 { " > " }
0 { " = " }
-1 { " < " }
}
}
$tuple = str_to_tuple("1.1")
$tuple2 = str_to_tuple("1.1.0.0")
$tuple3 = str_to_tuple("3.3")
$tuple4 = str_to_tuple("2.2.2")
$tuple5 = str_to_tuple("2.2.2.0.1")
vers_output(comp_vers $tuple $tuple) # 1.1 = 1.1
vers_output(comp_vers $tuple2 $tuple) # 1.1.0.0 = 1.1
vers_output(comp_vers $tuple $tuple3) # 1.1 < 3.3
vers_output(comp_vers $tuple3 $tuple4) # 3.3 > 2.2.2
vers_output(comp_vers $tuple4 $tuple5) # 2.2.2 < 2.2.2.0.1