I have an executable that does a bunch of processing and then waits for a keypress. I'm trying to run this Powershell script from the Windows scheduler. Here's my original attempt:
$logFile = "E:\BatchFiles\Log\JustWaitKey.txt"
"We're about to run out test program" >> $logFile
$process = Start-Process -FilePath "E:\BatchFiles\JustWaitKey.exe" >> $logFile -WorkingDirectory E:\BatchFiles -PassThru
Start-Sleep -Seconds 5
Stop-Process -Id $process.Id
"Our test program has completed" >> $logFile
The problem here is that it throws an error on the Stop-Process step because the parameter is null.
HOWEVER, if I remove the ">> $logFile" parameter after the ...JustWaitKey.exe, the program runs and when it finishes, it is stopped! I do see the two messages going to the $logFile.
The problem with this is that I don't get the console output from the JustWaitKey.exe program?
I have an executable that does a bunch of processing and then waits for a keypress. I'm trying to run this Powershell script from the Windows scheduler. Here's my original attempt:
$logFile = "E:\BatchFiles\Log\JustWaitKey.txt"
"We're about to run out test program" >> $logFile
$process = Start-Process -FilePath "E:\BatchFiles\JustWaitKey.exe" >> $logFile -WorkingDirectory E:\BatchFiles -PassThru
Start-Sleep -Seconds 5
Stop-Process -Id $process.Id
"Our test program has completed" >> $logFile
The problem here is that it throws an error on the Stop-Process step because the parameter is null.
HOWEVER, if I remove the ">> $logFile" parameter after the ...JustWaitKey.exe, the program runs and when it finishes, it is stopped! I do see the two messages going to the $logFile.
The problem with this is that I don't get the console output from the JustWaitKey.exe program?
2 Answers
Reset to default 1The redirection on Start-Process
is wrong (>> $logFile
), the cmdlet already has a -RedirectStandardOutput
parameter for this. So what you could do is first start the process at let it redirect the output, then after killing it, you can get the content and append the additional information you wanted:
$logFile = 'E:\BatchFiles\Log\JustWaitKey.txt'
$startProcessSplat = @{
FilePath = 'E:\BatchFiles\JustWaitKey.exe'
RedirectStandardOutput = $logFile
WorkingDirectory = 'E:\BatchFiles'
PassThru = $true
}
$process = Start-Process @startProcessSplat
Start-Sleep -Seconds 5
$process | Stop-Process
@(
"We're about to run out test program"
Get-Content $logFile -Raw
"Our test program has completed"
) | Set-Content $logFile
If you want your process to stream its output directly to the file, appending to it, then you need to take a much more manual approach calling the .NET APIs directly:
$logFile = 'E:\BatchFiles\Log\JustWaitKey.txt'
$process = [System.Diagnostics.Process]@{
StartInfo = [System.Diagnostics.ProcessStartInfo]@{
FileName = 'E:\BatchFiles\JustWaitKey.exe'
WorkingDirectory = 'E:\BatchFiles'
UseShellExecute = $false
RedirectStandardOutput = $true
RedirectStandardError = $true
}
}
$handler = { Add-Content -LiteralPath $Event.MessageData -Value $EventArgs.Data }
$stdout = Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -Action $handler -MessageData $logFile
$stderr = Register-ObjectEvent -InputObject $process -EventName ErrorDataReceived -Action $handler -MessageData $logFile
Register-ObjectEvent -InputObject $process -EventName Exited -SourceIdentifier Process.Exited
try {
Add-Content -LiteralPath $logFile -Value "We're about to run out test program"
$null = $process.Start()
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
Start-Sleep -Seconds 5
$process.Kill()
Wait-Event -SourceIdentifier Process.Exited | Unregister-Event
Add-Content -LiteralPath $logFile -Value 'Our test program has completed'
}
finally {
$process.Dispose()
$stdout, $stderr | Unregister-Event -SourceIdentifier { $_.Name }
}
To add to Santiago's helpful answer:
Fundamentally, the following cannot work in PowerShell:
# !! $capturedOutput is *invariably "nothing"*
$capturedOutput = ... >> $logFile ...
Both a variable assignment and a redirection such as
>
or>>
operate on PowerShell's success output stream; stdout output from external programs is routed through this stream in that case.If both are used in a single statement, the redirection takes precedence, and "nothing" is assigned to the target variable.
- Strictly speaking, the enumerable null (
[System.Management.Automation.Internal.AutomationNull]::Value
) is assigned, which in expression contexts behaves like$null
.[1]
- Strictly speaking, the enumerable null (
If the target program expects a keypress, you may be able to provide one via stdin, (which in PowerShell means using the pipeline), so the following may allow you to directly, synchronously execute your external program without having to explicitly kill the process after a fixed timeout:
# Send dummy keypress
'y' | E:\BatchFiles\JustWaitKey.exe >> $logFile
Note:
This assumes that
JustWaitKey.exe
doesn't explicitly clear the keyboard buffer before waiting for a keypress.If switching to a specific working directory is a must (as your
Start-Process
call suggests), use the following variation:Push-Location E:\BatchFiles 'y' | .\JustWaitKey.exe >> $logFile Pop-Location
[1] See this answer for more information.