使用python样式元组在Powershell中进行比较

时间:2019-02-10 02:12:56

标签: python powershell

我正在尝试将此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

非常感谢任何指导。

2 个答案:

答案 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.MLee_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