A few general points:
Write-Host
is typically the wrong tool to use, unless the intent is to write to the display only, bypassing the success output stream and with it the ability to send output to other commands, capture it in a variable, or redirect it to a file. To output a value, use it by itself; e.g, $value
, instead of Write-Host
$value
(or use Write-Output
$value
, but that is rarely needed); see this answer. To explicitly print only to the display but with rich formatting, use Out-Host
.
Therefore, to get proper output formatting, just use $_.Name
and $camModel | Select-Object -Property "CameraModel"
as-is - no need for Write-Host
or even Write-Output
, which is implied. If you want just the camera model value, use -ExpandProperty
instead of Property
.
As for your specific questions:
how do I check if this is blank, as in the bottom result @{CameraModel=}
$md = Get-FileMetaData -File $_.FullName
# Just use property access to get the camera model *value*.
# If no such property exists, $null is returned
# (except if Set-StrictMode -Version 2 or above is in effect).
$camModel = $md.CameraModel
if ($camModel -like '' {
"Camera model is $null or the empty string."
}
What does @{CameraModel=}
actually mean? What is the @{}
signify?
This hashtable-like representation is unrelated to actual hashtables and not meant for programmatic processing.
PowerShell uses this for-display representation when [pscustomobject]
instances, such as created by Select-Object
, are coerced to strings, such as when you use Write-Host
- see this answer for more information.
Optional reading: Pitfall when using Select-Object
with -ExpandProperty
for extracting property values:
The above solution uses simple property access (dot notation) to get the value of the $md
object's .CameraModel
property, i.e. $md.CameraModel
This syntactically simple approach even works when directly applied to expressions and commands enclosed in (...)
, and even when the object being operated on is a collection of objects, due to a feature called member-access enumeration); e.g., in the following example the .Year
property values of the input objects are returned as an array ([object[]]
):
((Get-Date), (Get-Date).AddYears(1)).Year # e.g. -> @(2022, 2023)
If the return values are to be collected in memory anyway, there is therefore no good reason to achieve the same tasks via a - invariably slower - Select-Object -ExpandProperty
call; e.g.:
# Same result as above.
(Get-Date), (Get-Date).AddYears(1) | Select-Object -ExpandProperty Year
Another reason to avoid Select-Object -ExpandProperty
is an inconsistency you've discovered:
Unlike with -Property
, -ExpandProperty
reports an error for any input object that happens not to have that property; e.g.:
# -> Outputs 1, for the first object, but then emits an
# ERROR: 'Select-Object: Property "Prop" cannot be found.'
[pscustomobject] @{ Prop=1 }, [pscustomobject] @{ } |
Select-Object -ExpandProperty Prop
With -Property
, by contrast, you'd get a [pscsutomobject]
instance whose .Prop
value is $null
for any input object that doesn't have a .Prop
property - no error occurs.
This surprising inconsistency is the subject of GitHub issue #18416. [Update: a decision was made not to change this behavior and merely document it.]
Strict mode (Set-StrictMode
) considerations:
As a cmdlet, Select-Object
is not affected by the strict mode that is in effect:
- The (unexpected) errors that occur with
-ExpandProperty
for non-existent properties are reported per input object (and are therefore non-terminating errors), which means that values are still reported for any input objects that do have the requested property.
By contrast, attempts to access a non-existent property (with .
, the member-access operator) only cause an error if Set-StrictMode -Version 2
or higher is in effect:
Using .
to access a non-existent property results in a statement-terminating error, which means that the entire statement is instantly terminated, with the attempted property access producing no (data) output.
Since this also applies to member-access enumeration, there is no (data) output even if some of the input objects have the targeted property - unlike with Select-Object -ExpandProperty
; e.g.:
# Set strict mode to a version that enforces property existence.
Set-StrictMode -Version 2
# !! This ONLY produces an ERROR - it doesn't also output
# !! 1, even though the first input object has a .Prop property.
([pscustomobject] @{ Prop=1 }, [pscustomobject] @{ }).Prop