【问题标题】:PowerShell PipelineVariable parameter contains only first value in a collection of PSCustomObjectPowerShell PipelineVariable 参数仅包含 PSCustomObject 集合中的第一个值
【发布时间】:2018-11-05 02:56:39
【问题描述】:

问题

我只得到PSCustomObject 类型数组中一个文件的代码行数。使用以下消息将数组错误中的所有条目恢复 -

Get-Content : 无法将参数绑定到参数“路径”,因为它为空。

在 line:42 char:100

... -Content -Path $files.UncommentedFileName) |测量-对象-线 |选择对象...

如何克服此错误并显示数组中所有文件的代码行?

约束

  1. 我打算将函数Remove-VBComments 重新用于必须处理VB6 文件的其他场景。因此,我无法避免定义和使用Remove-VBComments
  2. 我打算让函数 Remove-VBComments 接受流水线输入,因为它对流水线友好,以便在其他 cmdlet 可以在管道中 Remove-VBComments 之后运行的场景中使用
  3. 我在客户环境中被限制使用 PowerShell 4.0 版。

详情如下--

意图

我的代码是总结从 cmets 中剥离的 VB6 代码文件的代码行。评论始终是 single ',不提供多行 cmets。遵循 PowerShell 脚本是我实现这一意图的尝试。

PowerShell 代码

$filesToStripComment = @([PSCustomObject]@{
"SourceFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\App.cls";
"UncommentedFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\App.cls.uncommented"
},[PSCustomObject]@{
"SourceFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\Class1.cls";
"UncommentedFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\Class1.cls.uncommented"
},[PSCustomObject]@{
"SourceFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\IFilterReporter.cls";
"UncommentedFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\IFilterReporter.cls.uncommented"
},[PSCustomObject]@{
"SourceFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\ITemplateFilter.cls";
"UncommentedFileName" = ".\vb6-master\ClassTemplate\ClassBuilder\ITemplateFilter.cls.uncommented"
});

Function Remove-VBComments {
    [CmdletBinding()]
    Param(
        [Parameter(
            Mandatory = $True,
            ValueFromPipelineByPropertyName=$True
        )]
        [String]
        $SourceFileName,

        [Parameter(
            Mandatory = $True,
            ValueFromPipelineByPropertyName=$True
        )]
        [String]
        $UncommentedFileName
    )
    Process {
        (Get-Content -LiteralPath $SourceFileName) `
            -replace "\'.*$",$ReplaceString -notmatch "^\s*$" `
        | Out-File -LiteralPath     $UncommentedFileName;
        $result =  [PSCustomObject]@{
                        SourceFileName=$($SourceFileName); 
                        UncommentedFileName=$($UncommentedFileName)
                    };
        Write-Output $result;
    }
}

$filesToStripComment | `
    Remove-VBComments -PipelineVariable "files" | `
    &{Process{ `
               (Get-Content -Path $files.UncommentedFileName) `
                  | Measure-Object -Line `
                  | Select-Object -Property `
                      @{n="FileName";e={$files.UncommentedFileName}}, ` 
                      @{n="LoC";e={$_.Lines}} `
     } }

cls 文件是我从 github 项目中复制的文件。

以上代码的输出

FileName                                                          LoC
-------------                                                     -------
.\vb6-master\ClassTemplate\ClassBuilder\App.cls.uncommented       29

Get-Content : Cannot bind argument to parameter 'Path' because it is null.
At line:42 char:100
...... 
# The above error repeats for all other entries in the array $filesToStripComment.

观察 #1

如果我在调用 Cmdlet Remove-VBComments 后进行以下小的更改,我将获得数组 $filesToStripComment 中所有文件的 LoC 计数。

... | Remove-VBComments | Select-Object -PipelineVariable "files" | `
& { Process { (Get-Content -Path $files.UncommentedFileName) | `
Measure-Object -Line | `
Select-Object -Property @{n="FileName",e={$files.UncommentedFileName} ...

现在我不明白为什么管道Select-Object 的这种细微变化让我得到了想要的结果,但在 Cmdlet Remove-VBComments 上使用 PipelineVariable 却没有得到结果?!

观察 #2

同时,我通过引入一个新的 Cmdlet Get-ClsFilesRecursively 即兴编写了代码,而不是传递 PSCustomObject 的数组。函数看起来像这样 -

Function Get-ClsFilesRecursively{
[CmdletBinding()]
Param(
      [Parameter(
        Mandatory=$True,
        ValueFromPipelineByPropertyName=$True,
      )]
      [String]
      $SourceCodePath
)
  Process{
    Write-Output (Get-ChildItem -Path $SourceCodePath `
                              -Filter "*.cls" `
                              -Recurse | `
               Select-Object -Property `
                    @{n="LiteralPath";e={$_.FullName}});
  }
}

要使用此功能,我会这样调用

Get-ClsFilesRecursively -SourceCodePath . -PipelineVariable "files" | `
& { Process { `
   Get-Content -LiteralPath $files.LiteralPath | `
   Measure-Object -Line | `
   Select-Object -Property @{n="File name";e={$files.LiteralPath}} `
                        @{n="LoC"; e={$_.Lines}}
}}

现在这给了我目录中所有文件的代码行;无一例外。但是,此调用在文件中包含 cmets。现在,如果我在上面的调用序列中通过管道传递Remove-VBCommments,我会回到上面问题中提到的问题。 我对导致Remove-VBComments 无法与PipelineVariable 正常工作而其他Cmdlet 与PipelineVariable 正常工作的原因感到困惑!

请帮忙!

【问题讨论】:

    标签: powershell pipeline powershell-4.0


    【解决方案1】:

    很遗憾,-PipelineVariable 不适用于像 Remove-VBComments 这样的脚本 cmdlet。这就是为什么将 if 转移到 Select-Object 起作用的原因 - 这是一个 C# cmdlet。但是您实际上并不需要它,因为 foreach 循环中的 $_ 包含 $files 变量中的所有信息。更新代码看起来像

    $filesToStripComment |
        Remove-VBComments |
        &{Process{
                    $UncommentedFileName = $_.UncommentedFileName
                    Get-Content -Path $UncommentedFileName |
                        Measure-Object -Line |
                            Select-Object -Property `
                                @{n="FileName";e={$UncommentedFileName}}, 
                                @{n="LoC";e={$_.Lines}} `
         } }
    

    【讨论】:

    • 感谢您指出 $_ 上下文变量。但是,我无法找到 PipelineVariable 失败的原因。请遵守上述编辑中的观察#2。脚本 cmdlet 似乎支持 PipelineVariable 但是我怀疑我没有正确编写 Remove-VBComments Cmdlet。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-05
    • 2022-01-05
    • 2021-12-29
    • 2018-12-10
    • 2020-08-25
    相关资源
    最近更新 更多