最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

process - Powershell script to kill it after it runs AND capture standard output - Stack Overflow

programmeradmin1浏览0评论

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?

Share edited Mar 7 at 23:02 mklement0 443k68 gold badges711 silver badges928 bronze badges asked Mar 7 at 20:10 CraigCraig 3511 gold badge5 silver badges19 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

The 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]

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.

发布评论

评论列表(0)

  1. 暂无评论