PowerShell PipelineVariable参数仅包含PSCustomObject

时间:2018-05-25 16:52:57

标签: powershell pipeline powershell-v4.0

问题

我只计算PSCustomObject类型数组中的一个文件的代码行数。使用以下消息 -

将数组中的所有条目错误输出
  

Get-Content:无法将参数绑定到参数'Path',因为它为null。

     

在行:42字符:100

     

... -Content -Path $ files.UncommentedFileName)| Measure-Object -Line | Select-Objec ......

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

约束

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

    意图

    我的代码是总结一行VB6代码文件的代码行,该文件从注释中删除。评论始终为single ',不提供多行评论。以下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

    我没有传递PSCustomObject数组,而是通过引入新的Cmdlet Get-ClsFilesRecursively来实现代码。该函数看起来像这样 -

    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}}
    }}
    

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

    请帮忙!

1 个答案:

答案 0 :(得分:1)

不幸的是,-PipelineVariable无法使用Remove-VBComments等脚本cmdlet。这就是为什么移动到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}} `
     } }