web-dev-qa-db-ja.com

-Waitの代わりにStart-ProcessとWaitForExitを使用してExitCodeを取得する

PowerShellからプログラムを実行し、終了を待ってから、ExitCodeにアクセスしようとしていますが、あまり運がありません。バックグラウンドで実行するには処理が必要なので、-WaitStart-Process と一緒に使用したくありません。

簡単なテストスクリプトを次に示します。

cd "C:\Windows"

# ExitCode is available when using -Wait...
Write-Host "Starting Notepad with -Wait - return code will be available"
$process = (Start-Process -FilePath "notepad.exe" -PassThru -Wait)
Write-Host "Process finished with return code: " $process.ExitCode

# ExitCode is not available when waiting separately
Write-Host "Starting Notepad without -Wait - return code will NOT be available"
$process = (Start-Process -FilePath "notepad.exe" -PassThru)
$process.WaitForExit()
Write-Host "Process exit code should be here: " $process.ExitCode

このスクリプトを実行すると、 Notepad が開始されます。これを手動で閉じた後、終了コードが出力され、-waitを使用せずに再び開始されます。これを終了すると、ExitCodeは提供されません。

Starting Notepad with -Wait - return code will be available
Process finished with return code:  0
Starting Notepad without -Wait - return code will NOT be available
Process exit code should be here:

プログラムを開始してから終了するまでの間に追加の処理を実行できるようにする必要があるため、-Waitを使用できません。どうすればこれを行うことができ、このプロセスから.ExitCodeプロパティにアクセスできますか?

57
Richard

あなたにできる2つのこと...

  1. System.Diagnostics.Processオブジェクトを手動で作成し、Start-Processをバイパスします
  2. バックグラウンドジョブで実行可能ファイルを実行します(非対話型プロセスのみ!)

以下のいずれかを実行できます。

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "notepad.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = ""
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
#Do Other Stuff Here....
$p.WaitForExit()
$p.ExitCode

または

Start-Job -Name DoSomething -ScriptBlock {
    & ping.exe somehost
    Write-Output $LASTEXITCODE
}
#Do other stuff here
Get-Job -Name DoSomething | Wait-Job | Receive-Job
40
Andy Arismendi

ここで覚えておくべきことが2つあります。 1つは-PassThru引数を追加することで、2つは-Wait引数を追加することです。この欠陥のため、待機引数を追加する必要があります。 http://connect.Microsoft.com/PowerShell/feedback/details/520554/start-process-does-not-return-exitcode-property

-PassThru [<SwitchParameter>]
    Returns a process object for each process that the cmdlet started. By d
    efault, this cmdlet does not generate any output.

これを行うと、プロセスオブジェクトが返され、そのオブジェクトのExitCodeプロパティを確認できます。以下に例を示します。

$process = start-process ping.exe -windowstyle Hidden -ArgumentList "-n 1 -w 127.0.0.1" -PassThru -Wait
$process.ExitCode

# This will print 1

-PassThruまたは-Waitなしで実行すると、何も出力されません。

同じ答えがここにあります:PowerShellでWindowsインストーラーを実行して成功/失敗値を取得する方法

81

上記の最後の提案を試している間に、私はさらに簡単な解決策を発見しました。プロセスハンドルをキャッシュするだけでした。それをするとすぐに、$ process.ExitCodeは正しく機能しました。プロセスハンドルをキャッシュしなかった場合、$ process.ExitCodeはnullでした。

例:

$proc = Start-Process $msbuild -PassThru
$handle = $proc.Handle # cache proc.Handle
$proc.WaitForExit();

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
32
Adrian

「-Wait」オプションは、プロセスが終了したにもかかわらず、ブロックされているように見えました。

私はエイドリアンのソリューションを試してみましたが、うまくいきました。しかし、プロセスハンドルを取得する副作用に依存する代わりに、 Wait-Process を使用しました。

そう:

$proc = Start-Process $msbuild -PassThru
Wait-Process -InputObject $proc

if ($proc.ExitCode -ne 0) {
    Write-Warning "$_ exited with status code $($proc.ExitCode)"
}
8
blt

または、これを追加してみてください...

$code = @"
[DllImport("kernel32.dll")]
public static extern int GetExitCodeProcess(IntPtr hProcess, out Int32 exitcode);
"@
$type = Add-Type -MemberDefinition $code -Name "Win32" -Namespace Win32 -PassThru
[Int32]$exitCode = 0
$type::GetExitCodeProcess($process.Handle, [ref]$exitCode)

このコードを使用することで、PowerShellにリダイレクトされた出力/エラーストリームの管理を任せることができますが、System.Diagnostics.Process.Start()を直接使用することはできません。

4
Greg Vogel