51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Powershell – 输出版本号前格式化它

英文:

Powershell - format version number before outputting it

问题 {#heading}

以下是代码部分的翻译:

function ConvertFrom-FixedColumnTable {
  [CmdletBinding()]
  param(
    [Parameter(ValueFromPipeline)] [string] $InputObject
  )
  
  begin {
    Set-StrictMode -Version 1
    $lineNdx = 0
  }
  
  process {
    $lines = 
      if ($InputObject.Contains("`n")) { $InputObject.TrimEnd("`r", "`n") -split '\r?\n' }
      else { $InputObject }
    foreach ($line in $lines) {
      ++$lineNdx
      if ($lineNdx -eq 1) { 
        # 头部行
        $headerLine = $line 
      }
      elseif ($lineNdx -eq 2) { 
        # 分隔行
        # 获取字段开始的索引。
        $fieldStartIndices = [regex]::Matches($headerLine, '\b\S').Index
        # 计算字段长度。
        $fieldLengths = foreach ($i in 1..($fieldStartIndices.Count-1)) { 
          $fieldStartIndices[$i] - $fieldStartIndices[$i - 1] - 1
        }
        # 获取列名
        $colNames = foreach ($i in 0..($fieldStartIndices.Count-1)) {
          if ($i -eq $fieldStartIndices.Count-1) {
            $headerLine.Substring($fieldStartIndices[$i]).Trim()
          } else {
            $headerLine.Substring($fieldStartIndices[$i], $fieldLengths[$i]).Trim()
          }
        } 
      }
      else {
        # 数据行
        $oht = [ordered] @{} # 用于对象构造的有序帮助哈希表。
        $i = 0
        foreach ($colName in $colNames) {
          $oht[$colName] = 
            if ($fieldStartIndices[$i] -lt $line.Length) {
              if ($fieldLengths[$i] -and $fieldStartIndices[$i] + $fieldLengths[$i] -le $line.Length) {
                $line.Substring($fieldStartIndices[$i], $fieldLengths[$i]).Trim()
              }
              else {
                $line.Substring($fieldStartIndices[$i]).Trim()
              }
            }
          ++$i
        }
        # 将帮助哈希表转换为对象并输出它。
        [pscustomobject] $oht
      }
    }
  }
}

  
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() 

(winget list --name "ourSoft") -match '^(\p{L}|-)' | # 过滤掉进度显示行
ConvertFrom-FixedColumnTable |    # 解析输出为对象
Select-Object -Property Name, Version | # 仅显示名称和版本
Sort-Object Name |                  # 按名称属性(列)排序
Format-Table                      # 以表格形式显示对象

以下是第二个代码部分的翻译:

function ModifyVersionNumber {	
	$VersionNumber = Select-Object -Property Version
	$CharArray = $VersionNumber.Split(".")
	$ModifiedCharArray = $CharArray[0] + "." + $CharArray[1] + "." + $CharArray[3] + "." + $CharArray[2]
	$ModifiedCharArray	
}
  
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() 

(winget list --name "ourSoft") -match '^(\p{L}|-)' | # 过滤掉进度显示行
ConvertFrom-FixedColumnTable |    # 解析输出为对象 
ModifyVersionNumber | 
Select-Object -Property Name, Version | # 仅显示名称和版本
Sort-Object Name |                  # 按名称属性(列)排序
Format-Table                      # 以表格形式显示对象

希望这些翻译对你有所帮助。 英文:

I am trying to write a script, which will return a formatted version number.

However: due to winget outputting text, I have to first convert it to object, using the

Then I can get rid of the ID, which I have no use for and display only name and version.

Here's the code, taken from this answer:

function ConvertFrom-FixedColumnTable {
  [CmdletBinding()]
  param(
    [Parameter(ValueFromPipeline)] [string] $InputObject
  )

begin {
Set-StrictMode -Version 1
$lineNdx = 0
}


process {
$lines =
if ($InputObject.Contains(\"`n")) { $InputObject.TrimEnd("`r\", \"\`n\") -split \'\\r?\\n\' }
else { $InputObject }
foreach ($line in $lines) {
++$lineNdx
if ($lineNdx -eq 1) {
# header line
$headerLine = $line
}
elseif ($lineNdx -eq 2) {
# separator line
# Get the indices where the fields start.
$fieldStartIndices = \[regex\]::Matches($headerLine, \'\\b\\S\').Index
# Calculate the field lengths.
$fieldLengths = foreach ($i in 1..($fieldStartIndices.Count-1)) {
$fieldStartIndices\[$i\] - $fieldStartIndices\[$i - 1\] - 1
}
# Get the column names
$colNames = foreach ($i in 0..($fieldStartIndices.Count-1)) {
if ($i -eq $fieldStartIndices.Count-1) {
$headerLine.Substring($fieldStartIndices\[$i\]).Trim()
} else {
$headerLine.Substring($fieldStartIndices\[$i\], $fieldLengths\[$i\]).Trim()
}
}
}
else {
# data line
$oht = \[ordered\] @{} # ordered helper hashtable for object constructions.
$i = 0
foreach ($colName in $colNames) {
$oht\[$colName\] =
if ($fieldStartIndices\[$i\] -lt $line.Length) {
if ($fieldLengths\[$i\] -and $fieldStartIndices\[$i\] + $fieldLengths\[$i\] -le $line.Length) {
$line.Substring($fieldStartIndices\[$i\], $fieldLengths\[$i\]).Trim()
}
else {
$line.Substring($fieldStartIndices\[$i\]).Trim()
}
}
++$i
}
# Convert the helper hashable to an object and output it.
\[pscustomobject\] $oht
}
}
}


}


\[Console\]::OutputEncoding = \[System.Text.UTF8Encoding\]::new()

`(winget list --name "ourSoft") -match '^(\p{L}|-)' | # filter out progress-display lines
ConvertFrom-FixedColumnTable |    # parse output into objects
Select-Object -Property Name, Version | # show only Name and Version
Sort-Object Name |                  # sort by the Name property (column)
Format-Table                      # display the objects in tabular format
`

I am trying to do the last step, which will return a version number different in what is stored in OS:

  • 5.4.33676.0 - stored number in OS

  • 5.4.0.33676 - what I want to return (part 3 and 4, divided by a dot, are swapped)

Is there any way to do that?

I am trying to write my own function for that, but it seems CharArray is not filling up + I don´t know how to make the piped output display the modified numbers instead of the former ones:

function ModifyVersionNumber {	
	$VersionNumber = Select-Object -Property Version
	$CharArray =$VersionNumber.Split(".")
	$ModifiedCharArray = $CharArray[0] + "." + $CharArray[1] + "." + $CharArray[3] + "." + $CharArray[2]
	$ModifiedCharArray	
}

\[Console\]::OutputEncoding = \[System.Text.UTF8Encoding\]::new()

`(winget list --name "ourSoft") -match '^(\p{L}|-)' | # filter out progress-display lines
ConvertFrom-FixedColumnTable |    # parse output into objects
ModifyVersionNumber |
Select-Object -Property Name, Version | # show only Name and Version
Sort-Object Name |                  # sort by the Name property (column)
Format-Table                      # display the objects in tabular format
`

答案1 {#1}

得分: 2

如果我尝试运行您提供的函数来处理测试对象,我会得到为什么它不起作用的线索:

$o = [pscustomobject]@{version = '5.4.0.33676'};
$o | ModifyVersionNumber;

给我返回的信息是:

无法对空值表达式调用方法。
在行:3字符:5
+     $CharArray =$VersionNumber.Split(""."")
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

无法索引到空数组。
在行:4字符:5
+     $ModifiedCharArray = $CharArray[0] + ""."" + $CharArray[1] + ""."" +  ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

初始的 select-object 没有任何可操作的内容。我重新编写了该函数以从管道中获取参数,并且对于我的测试案例可以正常工作:

function ModifyVersionNumber {
    param ([parameter(ValueFromPipeline = $true)]$Versions)
    process{
        foreach ($obj in $Versions) {
            $VersionNumber = $obj.Version;
            $CharArray =$VersionNumber.Split(""."")
            $ModifiedCharArray = $CharArray[0,1,3,2] -join '.'
            $obj.Version = $ModifiedCharArray;
            $obj;
        }
    }
}

另外,请考虑以下内容:$ary = @(1,2,3,4); $ary[0,1,3,2] -join '.' 可以帮助大大减少您重新组装版本字符串的代码。 英文:

If I try to run your function as provided against a test object, I get a clue as to why it's not working:

$o = [pscustomobject]@{version = '5.4.0.33676'};
$o | ModifyVersionNumber;

gives me:

You cannot call a method on a null-valued expression.
At line:3 char:5
+     $CharArray =$VersionNumber.Split(".")
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Cannot index into a null array.
At line:4 char:5

`
`
* 

      $ModifiedCharArray = $CharArray[0] + "." + $CharArray[1] + "." +  ...






* `
  `

      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


  `
  ``
  `
  * CategoryInfo          : InvalidOperation: (:) [], RuntimeException


  * `FullyQualifiedErrorId : NullArray
    `

That initial select-object doesn't have anything to operate on. I rewrote the function to take a parameter from the pipeline and it works for my test case:

function ModifyVersionNumber {
  param ([parameter(ValueFromPipeline = $true)]$Versions)
  process{
      foreach ($obj in $Versions) {
          $VersionNumber = $obj.Version;
          $CharArray =$VersionNumber.Split(".")
          $ModifiedCharArray = $CharArray[0,1,3,2] -join '.'
          $obj.Version = $ModifiedCharArray;
          $obj;
      }
  }


`}
`

Also, for your consideration: $ary = @(1,2,3,4); $ary[0,1,3,2] -join '.' would help to significantly reduce the code where you're re-assembling the version string.

答案2 {#2}

得分: 0

有多种方法可以处理这个问题。您可以将版本字符串拆分为其各个组件,然后手动按正确的顺序将它们组合在一起:

$version = '5.4.33676.0'
$major, $minor, $build, $revision = $version.Split('.')
$version = $major, $minor, $revision, $build -join '.'

或者您可以使用 -replace 正则表达式运算符来替换最后两个组件:

$version = '5.4.33676.0'
$version = $version -split '\.(\d+)\.(\d+)$', '.$2.$1'

英文:

There's a number of ways to go about this. You could split the version string into its individual components and then manually join them together in the right order:

$version = '5.4.33676.0'
$major,$minor,$build,$revision = $version.Split('.')
$version = $major,$minor,$revision,$build -join '.'

Or you could use the -replace regex operator to replace the two last components with eachother:

$version = '5.4.33676.0'
$version = $version -split '\.(\d+)\.(\d+)$', '.$2.$1'

赞(1)
未经允许不得转载:工具盒子 » Powershell – 输出版本号前格式化它