英文:
Powershell - format version number before outputting it
问题
以下是代码部分的翻译:
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
得分: 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
得分: 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'
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论