PwSh RegEx 捕获版本信息 - 省略(周围)x(32|64|86) & 32|64(-)位

PwSh RegEx capture Version information - omit (surrounding) x(32|64|86) & 32|64(-)Bit

我很着急,RegEx-tract 一些文件名中的裸版本信息。 例如“1.2.3.4”

假设我有以下文件名:

VendorSetup-x64-1.23.4.exe
VendorSetup-1-2-3-4.exe
Vendor Setup 1.23.456Update.exe
SoftwareName-1.2.34.5-x64.msi
SoftwareName-1.2.3.4-64bit.msi
SoftwareName-64-Bit-1.2.3.4.msi
VendorName_SoftwareName_64_1.2.3_Setup.exe

(我知道那里还有一些文件名,其中包含“x32”和“x86”,所以我将它们添加到标题中)

首先,我将 _- 替换为 .,我通常希望避免这种情况,但还没有找到一个更聪明的方法,说实话 - 只有在字符串中没有其他“数字”信息时才有效,例如第二个文件名。

然后我尝试使用像

这样的正则表达式来提取版本信息
-replace '^(?:\D+)?(\d+((\.\d+){1,4})?)(?:.*)?', ''

无法省略“x64”、“64Bit”、“64-Bit”或一般情况下的任何变体。

此外,我还玩过正则表达式,例如

 -replace '^(?:[xX]*\d{2})?(?:\D+)?(\d+((\.\d+){1,4})?)(?:.*)?$', ''

尝试省略前导“x64”或“64”,但没有成功(很可能是因为从 - 替换为 .

在事情变得更糟之前,我想问一下是否有人可以帮助我或引导我朝着正确的方向前进?

提前致谢!

这可以使用单一模式来完成,但通过将其拆分为两个单独的模式并让 PowerShell 完成一些工作,整体解决方案会容易得多。

模式 1 匹配由 .(点)分隔的版本号:

(?<=[\s_-])\d+(?:\.\d+){1,3}

模式 2 匹配由 -(破折号)分隔的版本号:

(?<=[\s_-])\d+(?:-\d+){1,3}

模式以 (?<=[\s_-]) 开头,这是一个积极的后视断言,可确保版本由左侧的 space、下划线或短划线分隔,而不会将这些包含在捕获的值中.这可以防止第一个样本中的子字符串 64-1 作为一个版本进行匹配。

可以在 regex101.

中找到该模式的详细说明

Powershell代码:

# Create an array of sample filenames
$names = @'
VendorSetup-2022-05-x64-1.23.4.exe
VendorSetup-x64-1.23.4-2022-05.exe
VendorSetup-1-2-3-4.exe
VendorSetup_2022-05_1-2-3-4.exe
Vendor Setup 1.23.456Update.exe
SoftwareName-1.2.34.5-x64.msi
SoftwareName-1.2.3.4-64bit.msi
SoftwareName-64-Bit-1.2.3.4.msi
VendorName_SoftwareName_64_1.2.3_Setup.exe
NoVersion.exe
'@ -split '\r?\n'

# Array of RegEx patterns in order of precedence.
$versionPatterns = '(?<=[\s_-])\d+(?:\.\d+){1,3}',  # 2..4 numbers separated by '.'
                   '(?<=[\s_-])\d+(?:-\d+){1,3}'    # 2..4 numbers separated by '-'

foreach( $name in $names ) {

    $version = $versionPatterns.
        ForEach{ [regex]::Match( $name, $_, 'RightToLeft' ).Value }.   # Apply each pattern from right to left of string.
        Where({ $_ }, 'First').                                        # Get first matching pattern (non-empty value).
        ForEach{ $_ -replace '\D+', '.' }[0]                           # Normalize the number separator and get single string.

    # Output custom object for nice table formatting
    [PSCustomObject]@{ Name = $name; Version = $version }
}

输出:

Name                                       Version 
----                                       ------- 
VendorSetup-2022-05-x64-1.23.4.exe         1.23.4  
VendorSetup-x64-1.23.4-2022-05.exe         1.23.4  
VendorSetup-1-2-3-4.exe                    1.2.3.4 
VendorSetup_2022-05_1-2-3-4.exe            1.2.3.4 
Vendor Setup 1.23.456Update.exe            1.23.456
SoftwareName-1.2.34.5-x64.msi              1.2.34.5
SoftwareName-1.2.3.4-64bit.msi             1.2.3.4 
SoftwareName-64-Bit-1.2.3.4.msi            1.2.3.4 
VendorName_SoftwareName_64_1.2.3_Setup.exe 1.2.3   
NoVersion.exe   

Powershell代码解释:

  • 为了解决歧义 当一个文件名有多个模式匹配时,我们使用以下规则:
    • 带有 . 分隔符的版本优于带有 - 分隔符的版本。我们只需按此顺序应用模式,并在第一个模式匹配时停止。
    • 首选最右边的版本(通过将 RightToLeft 标志传递给 [regex]::Match())。
  • .ForEach.WherePowerShell intrinsic methods。它们基本上是 ForEach-ObjectWhere-Object cmdlet 的更快变体。
  • 最后一个 .ForEach 之后的索引 [0] 运算符是必需的,因为 .ForEach.Where 总是 return 数组,即使只有一个值,与行为 cmdlets 相反。