No error when selecting non-existing property

2019-02-25 23:30发布

问题:

I want PowerShell to throw an error when trying to select non-existing properties, but instead I get empty column as output. Example:

$ErrorActionPreference=[System.Management.Automation.ActionPreference]::Stop;
Set-StrictMode -Version 'Latest'
Get-Process *ex* | Select-Object Id,ProcessName,xxx

   Id ProcessName   xxx
   -- -----------   ---
 9084 explorer
11404 procexp

I wrote a script that is importing multiple text files by Import-Csv, but headers in those file may change, and I'll end up with empty columns being loaded to the system.

EDIT: This is how I'm checking if the headers match:

$csv = Import-Csv -Delimiter ';' -Path $file.FullName 
$FileHeaders = @(($csv | Get-Member -MemberType NoteProperty).Name) 
if (Compare-Object $ProperHeaders $FileHeaders) {'err'} else {'ok'}

I know that's the way PowerShell works, but Set-StrictMode documentation was indeed a little misleading for me, as @Matt mentioned. I just wish Select-Object had some kind of "-NoNewImplicitProps" or "-ReadOnlyPipeline" switch that would do the job for me :). Thanks for the answers.

回答1:

You are actually using what some people would call a feature. That is a simpler rendition of using Add-Member on all the array members to add an empty column.

In the case of Import-CSV what you do in that case is check the property names before the Select where you call them.

$data = Import-csv C:\Temp\file.csv 
$props = $data | Get-member -MemberType 'NoteProperty'  | Select-Object -ExpandProperty Name

I can see the documentation be a little misleading when it says for Set-StrictMode:

Prohibits references to non-existent properties of an object.

But in this case you are not trying to get the property reference but using a function of the Select-Object cmdlet. The following would have generated an error though

PS C:\Users\mcameron> Set-StrictMode -Version 'Latest'
(Get-Process *ex*).Bagels
The property 'Bagels' cannot be found on this object. Verify that the property exists.
At line:2 char:1
+ (Get-Process *ex*).Bagels
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], PropertyNotFoundException
    + FullyQualifiedErrorId : PropertyNotFoundStrict


回答2:

PowerShell expanding non-existing properties to $null behaves as designed. AFAICS the only thing you could do is to explicitly check if all properties exist:

$props = 'Id', 'ProcessName', 'xxx'

$p = Get-Process *ex*
$missing = $p | Get-Member -Type *property |
           Select-Object -Expand Name |
           Compare-Object -Reference $props |
           Where-Object { $_.SideIndicator -eq '<=' } |
           Select-Object -Expand InputObject

if ($missing) {
  throw "missing property $missing."
} else {
  $p | Select-Object $props
}

Of course you could wrap this in a custom function:

function Select-ObjectStrict {
  [CmdletBinding()]
  Param(
    [Parameter(
      Position=0,
      Mandatory=$true,
      ValueFromPipeline=$true,
      ValueFromPipelineByPropertyName=$true
    )]$InputObject,

    [Parameter(
      Position=1,
      Mandatory=$true
    )][string[]]$Property
  )

  Process {
    $missing = $InputObject | Get-Member -Type *property |
               Select-Object -Expand Name |
               Compare-Object -Reference $Property |
               Where-Object { $_.SideIndicator -eq '<=' } |
               Select-Object -Expand InputObject

    if ($missing) {
      throw "missing property $missing."
    } else {
      $InputObject | Select-Object $Property
    }
  }
}

so it could be used like this:

Get-Process *ex* | Select-ObjectStrict -Property 'Id', 'ProcessName', 'xxx'


回答3:

Something like this...?

$props = 'Id','ProcessName','xxx'
$availableProps = Get-Process *ex*|Get-Member -MemberType Properties | Select -ExpandProperty Name
$missingProps = $props | Where-Object {-not ($availableProps -contains $_)}
if ($missingProps) {
  Write-Error "invalid property(s) $missingProps"
  throw { [System.Management.Automation.PropertyNotFoundException] }
}

Get-Process *ex* | Select-Object $props