For a list of files (photos), I'm trying to check if they have EXIF info from a camera or not and only further process photos that aren't from a camera. I thought I would just check if the CameraModel EXIF prop is blank, but I'm stuck.
I have this code:
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
Write-Host $dir
Invoke-Expression -Command $($dir "\Get-FileMetaData.ps1")
Get-ChildItem -Path $dir -Directory |
Foreach-Object {
$date_str = $_.Name '-01-01 00:00:00'
$date = $(Get-Date -Date $date_str)
Write-Host $_.FullName
Get-ChildItem -Path $_.FullName -Filter "*.jpg" |
Foreach-Object {
Write-Host $_.Name
$md = Get-FileMetaData -File $_.FullName
$camModel = $($md | Select "CameraModel")
Write-Host $($camModel | Select-Object -Property "CameraModel")
}
}
Where Get-FileMetaData from from here https://evotec.xyz/getting-file-metadata-with-powershell-similar-to-what-windows-explorer-provides/ halfway down.
This returns
DSC_0132.JPG
@{CameraModel=NIKON D80}
ScannedImage001.JPG
@{CameraModel=}
My question is, how do I check if this is blank, as in the bottom result @{CameraModel=}
?
What does @{CameraModel=}
actually mean? What is the @{}
signify?
Edit: Thanks @mlkement0!
My final script is below, which sets the creation and last modified dates of non-camera JPGs to 1st Jan where the year is the year in the folder name:
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
Write-Host $dir
Invoke-Expression -Command $($dir "\Get-FileMetaData.ps1")
Get-ChildItem -Path $dir -Directory |
Foreach-Object {
$date_str = $_.Name '-01-01 00:00:00'
$date = $(Get-Date -Date $date_str)
Write-Host $_.FullName
Get-ChildItem -Path $_.FullName -Filter "*.jpg" |
Foreach-Object {
Write-Host $_.Name
$md = Get-FileMetaData -File $_.FullName
$camModel = $md.CameraModel
if ($camModel -like ''){
$(get-item $_.FullName).CreationTime = $date
$(get-item $_.FullName).LastWriteTime = $date
Write-Host $("Set date to " $date)
}
}
}
CodePudding user response:
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 ofWrite-Host
$value
(or useWrite-Output
$value
, but that is rarely needed); see this answer. To explicitly print only to the display but with rich formatting, useOut-Host
.Therefore, to get proper output formatting, just use
$_.Name
and$camModel | Select-Object -Property "CameraModel"
as-is - no need forWrite-Host
or evenWrite-Output
, which is implied. If you want just the camera model value, use-ExpandProperty
instead ofProperty
.
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.
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.
- The (unexpected) errors that occur with
By contrast, attempts to access a non-existent property (with
.
, the member-access operator) only cause an error ifSet-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