Running in Powershell function with exiftool if condition.

Started by QGtKMlLz, August 27, 2023, 12:57:53 PM

Previous topic - Next topic

QGtKMlLz

I have been having so much trouble with running exiftool correctly while creating a large powershell script for the last few months. I can only get it to run my command line appropriately Invoke-Expression.
My command:
    $formattedPaths = $metaFilePath | ForEach-Object { "'" + $_.Replace("'", "''") + "'" }
    # Join the paths into a single string, separated by spaces
    $joinedPaths = [String]::Join(' ', $formattedPaths)

    $commandString = "exiftool.exe -fast2 -charset UTF8 -if 'defined `$$firstMetaDataField' $joinedPaths -p ''"
    Invoke-Expression $commandString

My $firstMetaDataField PS var is a field like $ARTIST. Initially it was multiple fields but was having different troubles with feeding that through a variable too.

I am just looking for files passed and files failed (in this case using -p '' to filter the full field list output so just 'files failed' 'files read' are output for PS to read).

Has anyone run into this while working on Powershell code?

StarGeek

I'm  sorry, but I can't help with Powershell.  It has to many idiosyncrasies compared to every other shell and just works so badly with basic exiftool commands.
"It didn't work" isn't helpful. What was the exact command used and the output.
Read FAQ #3 and use that cmd
Please use the Code button for exiftool output

Please include your OS/Exiftool version/filetype

QGtKMlLz

I seem to have slightly narrowed it down more to the the single-quote use causing issue.

I have tried the below ways, and most of the time powershell seems to break the file path or present it to exiftool incorrectly and each spaced block get a "file not found" error.
$args = "-c exiftool.exe -fast2 -charset UTF8 -if 'defined $$firstMetaDataField' $joinedPaths -p ''"
Start-Process -FilePath 'powershell' -ArgumentList $args -Wait -NoNewWindow
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo.FileName = "exiftool.exe"
$proc.StartInfo.Arguments = "-fast2 -charset UTF8 -if 'defined `$$firstMetaDataField' $joinedPaths -p ''"
$proc.Start()
$proc.WaitForExit()
$runspace = [runspacefactory]::CreateRunspace()
$runspace.Open()
$powershell = [powershell]::Create().AddScript({
    & "exiftool.exe" -fast2 -charset UTF8 -if "defined `$$firstMetaDataField" $using:joinedPaths -p ''
})
$powershell.Runspace = $runspace
$powershell.BeginInvoke()
Invoke-Command -ScriptBlock {
    & "exiftool.exe" -fast2 -charset UTF8 -if "defined `$$firstMetaDataField" $using:joinedPaths -p ''
}

# Prepare the arguments as a single string first
$argString = "-fast2 -charset UTF8 -if 'defined $$firstMetaDataField' $joinedPaths -p ''"

# Tokenize the arguments string into an array
$argArray = $argString -split ' '

# Prepare ProcessStartInfo
$processStartInfo = New-Object System.Diagnostics.ProcessStartInfo
$processStartInfo.FileName = "exiftool.exe"
$processStartInfo.Arguments = $argArray
$processStartInfo.RedirectStandardOutput = $true
$processStartInfo.UseShellExecute = $false

# Start the process
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $processStartInfo
$process.Start() | Out-Null

# Capture and display the output
$output = $process.StandardOutput.ReadToEnd()
$process.WaitForExit()
$output
# Create a ProcessStartInfo object
$processStartInfo = New-Object System.Diagnostics.ProcessStartInfo

# Set the FileName to the executable's path
$processStartInfo.FileName = "exiftool.exe"

# Create an array of arguments to pass
$argsArray = @("-fast2", "-charset", "UTF8", "-if", "'defined `$$firstMetaDataField'", "$joinedPaths", "-p", "''")

# Convert the array to a single string, separating each element by a space
$processStartInfo.Arguments = [string]::Join(" ", $argsArray)

# Set additional properties
$processStartInfo.RedirectStandardOutput = $true
$processStartInfo.UseShellExecute = $false

# Create a new Process object and set its StartInfo property
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $processStartInfo

# Start the process
$process.Start() | Out-Null

# Read the standard output of the process
$output = $process.StandardOutput.ReadToEnd()

# Wait for the process to exit
$process.WaitForExit()

# Output the result
$output

QGtKMlLz

Solved:  Somehow with all the variation test in .ps1 and direct in console I thought I was running into the requirement that -if conditions had to be surrounded by single-quotes. I had ran into the that issue while running but other factors must have influenced it; I was told Powershell is very restrictive when single-quotes are used.

Here is a working high-performance powershell function for the record in case anyone needs it/runs into a similar issue with Powershell.

function HasMetadataFields ([Array]$metaFilePath) {
    # Enclose each file path with single quotes
    $formattedPaths = $metaFilePath | ForEach-Object { "`"" + $_.Replace("`"", "`"`"") + "`"" }
    # Join the paths into a single string, separated by spaces
    $joinedPaths = [String]::Join(' ', $formattedPaths)

    #.Net System.IO Approach for High-Performance
    $procStartInfo = [System.Diagnostics.ProcessStartInfo]::new()
    $procStartInfo.FileName = "exiftool.exe"
    $procStartInfo.Arguments = "-charset UTF8 -if ""defined `$$firstMetaDataField"" -fast2 -s3 -p'' $joinedPaths"
    [Console]::WriteLine("$($Cyan)$($procStartInfo.Arguments)$($Reset)")
    $procStartInfo.RedirectStandardOutput = $true
    $proc = [System.Diagnostics.Process]::Start($procStartInfo)

    # Set the priority
    $proc.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::Realtime

    #Run and Write to Console
    $output = $proc.StandardOutput.ReadToEnd()


    # Parse number of failed and total files
    # Evaluate output from exiftool.exe and if failed is greater-than 'read' s the if-statement will return a $false to the script.
    $failedConditionCount    = ([regex]::Match($output, '(\d+) files failed condition')).Groups[1].Value
    $imageFilesReadCount = ([regex]::Match($output, '(\d+) image files read')).Groups[1].Value

    if (!$failedConditionCount) { $consoleFailedCount = 0 }

    [Console]::WriteLine("Read Count: $($imageFilesReadCount) and failed count: $($consoleFailedCount)")

    # If there are any empty lines, some files are missing metadata fields, return $false
    if ($failedConditionCount -ne $null -and $imageFilesReadCount -ne $null -and $failedConditionCount -gt $imageFilesReadCount) {
        # Some Metadata is missing in metadata fields
        [Console]::WriteLine("HasMetaData is: False    but the count is: $($imageFilesReadCount)")
        return $false
    }
    else {
        [Console]::WriteLine("HasMetaData is: True    but the count is: $($imageFilesReadCount)")
        return $true
    }
}