Powershell: stay_open, StdOut, StdErr and other logging-options

Started by Rabiator, August 24, 2023, 09:42:31 AM

Previous topic - Next topic

Rabiator

Hello,

I'm currently working on a Powershell-script (WINDOWS) to reorganize my images (>90k), stored on different drives and devices. The images were shot by different persons who all had their own system of storing an file-naming.

I need some complex EXIF-editing and sorting (projects, folders, artists) and MUST avoid that existing duplicate filenames in different source-folders might collide by globally renaming-actions using EXIFTOOL. I have achieved this by building a kind of database that includes beside a lot of other information the wanted EXIF-Tag-values for every file, its current device/drive/folder, its future folder and its future filename.

I want to use Powershell 'start-process' cmdlet with the EXIFTOOL -stay_open option amd an argsfile. This works already in a basic version.

I'm struggeling with the EXIFTOOL StdOut and StdErr. I redirected both to textfiles and get the files written.

$ExifArgs = " -stay_open True -@ $($ArgsFilename3)"
$process = Start-Process -filepath "$($exiftool)" -NoNewWindow -PassThru -RedirectStandardOutput "$($ProcessStdOut)" -RedirectStandardError "$($ProcessErrOut)" -ArgumentList $ExifArgs

Here is a code-snippet that writes the -artist-tag and copies a file to a target-folder, but no renaming yet (for test purposes):

$counter = 0
$ms = 1000
$artist = 'Darth Vader'
$TestImage = $FolderNikon + $DefaultPathSep + $SourceFileNEF1
$TestOutputImage = $TargetRootFolder + $DefaultPathSep + $SourceFileNEF1

$ExifArgs = "-filename=$($TestOutputImage)" # target-file
$ExifArgs += "`n-artist=$($artist)" # artist
$ExifArgs += "`n$($TestImage)`n" # source-file
$ExifArgs | Out-File $ArgsFilename3 -Append # update argsfile
$counter++
"-execute$($counter.ToString())`n" | Out-File $ArgsFilename3 -Append # update argsfile
write-host "command $($counter.ToString())`nargs " $ExifArgs.Replace("`n"," ") "`npart 1/2 done $($counter.ToString())`n" # watch the workflow

Start-Sleep -Milliseconds $ms # wait a moment

#read data from created file to check if the operation was successful
$ExifArgs = "-s`n-filename`n-directory`n-artist`n-FileModifyDate`n$($TestOutputImage)" # new target-file
$ExifArgs | Out-File $ArgsFilename3 -Append # update argsfile
"-execute$($counter.ToString())`n" | Out-File $ArgsFilename3 -Append # update argsfile

write-host "command $($counter.ToString())`nargs " $ExifArgs.Replace("`n"," ") "`npart 2/2 done $($counter.ToString())`n" # watch the workflow

This code created the following StdOut-entry:

    1 image files created
{ready2}
FileName                        : D850_20230123-160415_.NEF
Directory                      : P:/atest/exif/temp/testrenamed
Artist                          : Darth Vader
FileModifyDate                  : 2023:08:24 13:49:32+02:00
{ready2}

If an error occured, the redirected StdErr-file gets a line with the error and the sourcefile-path.

I do not manage to use the -echo and the -efile options to get simple logs that I can use the easy way. Currently I have to read the StdOut- and StdErr-files and have to do a lot of string operations to get the information I need.

For me it would be sufficient if the StdOut-file shows the operation-number ($counter), the value of $TestImage (sourcefile) and the value of $TestOutputImage in a single line, maybe with a decent separator.

The Err-file should show the operation-number ($counter), the value of $TestImage (sourcefile) and the error-message in a single line.

Do you have any suggestions how to simplify the Std-outputs or how to integrate -echo and -efile options?

And I would be very happy it the documentation would be enhanced with some more examples how to use the -stay_open option with argsFiles and useful option-combinations and some examples for the -echo and the -efile options. The Internet has only very poor information about these options.

Btw, EXIFTOOL is a life-saver if you know how to use it. Thank you for this brilliant application.

Phil Harvey

First, I must say that I have never used PowerShell so I can't help much with the specifics there, but I do know that PowerShell will corrupt binary data in pipes, so I would avoid using it if possible.

The -echo option is very flexible, and its use depends entirely on what you are trying to accomplish.  You can use this option to write whatever you want to stdout or stderr.  -echo1 and -echo2 output before processing starts on the file(s), and -echo3 and -echo4 output after processing is finished.

The -efile option writes the specified filenames to a file.  Typically you wouldn't use this with -stay_open, and instead I would probably parse the stdout/stderr stream for the information you need here (possibly adding -v to get what you need).

Sorry I can't be more specific, but your post is very long and I'm trying to catch up after being away on vacation the last 3 weeks so I don't have time to read it all right now.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Beehaus

Hi @Rabinator i'm not sure if this is really answering your question, but it reads to me that you are trying to find the best way to capture the output of ExifTool into PowerShell
I found using .net method System.Diagnostics.ProcessStartInfo works better than start-process, combined with -json switch produces an object which can be worked with

here is a function I made which you might find useful, capturing output this way also benefits by running silently and faster which is what I was mainly trying to achieve at the time, and hopefully you can see how it can be modified from $stdout=$process.StandardOutput.ReadToEnd() to $stderr=$process.StandardError.ReadToEnd() if that is what you are trying to capture

Function ExifTool($Arguments){
    $processinfo = New-Object System.Diagnostics.ProcessStartInfo
    $processinfo.FileName = $exiftoolPath
    $processinfo.Arguments = $Arguments
    $processinfo.UseShellExecute = $false
    $processinfo.CreateNoWindow = $true
    $processinfo.RedirectStandardOutput = $true
    $processinfo.RedirectStandardError = $true
    $process= New-Object System.Diagnostics.Process
    $process.StartInfo = $processinfo
    [void]$process.Start()
    $stdout=$process.StandardOutput.ReadToEnd()
    return $stdout
}

$exiftoolPath = "C:\Exiftool.exe"
$filepath = "C:\IMG_0001.jpg"
$Arguments = """filepath"" -json"
ExifTool -Arguments $Arguments  | ConvertFrom-Json | % { $_.psobject.Properties | ? { $_.name -eq "syncroot" } | select -ExpandProperty Value }







scw2wi

Thanks a lot for this code, it was really helpful for me.

In my PowerShell script I had to add the following lines also:

    $processinfo.StandardOutputEncoding = [System.Text.Encoding]::UTF8
    $processinfo.StandardErrorEncoding  = [System.Text.Encoding]::UTF8

Walter