PowerShellでのStart-Process: 直感的なプロセス起動

Start-Processおよび他のコマンドが制限されている理由を理解するためには、まず、典型的なEXEファイルの動作を理解する必要があります。EXEファイルが実行されると、設計されたアクションを実行します。何かをpingする(ping)、ソフトウェアインストーラを起動する(setup)、DNSレコードを検索する(nslookup)などです。このようなプロセスを開始するためのStart-Processや他のコマンドは非常に便利です。簡単です。制限が発生するのは、そのEXEが何らかの出力を返す場合です。

PowerShellでは、さまざまな方法でプロセスを開始することができます。PowerShellにはStart-ProcessやInvoke-Expressionなどのコマンドレットがあり、実行可能ファイルを直接呼び出したり、アンパサンド(&)を使用して式を呼び出したりすることができます。最も一般的な方法は、おそらく最も直感的なStart-Processを使用する方法です。PowerShellはコマンドの命名方法において直感的なアプローチで知られており、Start-Processは優れた選択肢です。ただし、このコマンドには制限があります。

ストリーム

Windowsに限らず、他の実行可能ファイルと同様に、EXEファイルには「標準ストリーム」という概念があります。標準ストリームは、実行可能ファイルが出力を返す方法です。これらのストリームには、stdin、stdout、stderrという3つの種類があります。stdinは、ここでは扱わない実行可能ファイルに渡されるストリームです。stdoutは、実行可能ファイルが通常のエラーでない出力を送信するために使用するストリームです。

PowerShellの場合、stdoutはWrite-Outputが返すものと考えてください。エラーが発生した場合(実行可能ファイルの開発者が正しく書いたかどうかによる)、実行可能ファイルはstderrを通じて出力を返すべきです。これは、エラー情報を返すために確保されたストリームです。

これらのストリームは、これらの実行可能ファイルのユーザーが通常の出力とエラーを区別するのに役立ちます。ストリームは数十年前から存在しており、WindowsとPowerShellはそれについて詳しく知っており、独自のものを採用しています

終了コード

終了コード、リターンコード、または結果コードは、実行可能ファイルが終了時にユーザーにステータスを伝えるために従うもうひとつの古い概念です。

終了コードは、実行可能ファイルが終了するときにユーザーに信号を送るための整数です。終了コード0の場合はすべて正常であり、終了コード3010は再起動が必要なことを意味します。PowerShellは、$LastExitCodeのようないくつかの異なる方法でこの終了コードをキャプチャすることができます。終了コードは、実行可能ファイルがユーザーに伝えるための別の方法です。

ストリームと終了コードのキャプチャ

これで、私たちが取り組んでいるものを理解したので、例を使って説明します。簡単にするために、古くからあるping.exeを使用します。まず、成功した結果を返すgoogle.comにpingを実行します。ここではPowerShellのstart-processは使用しません。

PS C:\> ping google.com
Pinging google.com [2607:f8b0:4004:80b::200e] with 32 bytes of data:

Reply from 2607:f8b0:4004:80b::200e: time=61ms
Reply from 2607:f8b0:4004:80b::200e: time=65ms
Reply from 2607:f8b0:4004:80b::200e: time=80ms
Reply from 2607:f8b0:4004:80b::200e: time=78ms

Ping statistics for 2607:f8b0:4004:80b::200e:

Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 61ms, Maximum = 80ms, Average = 71ms

PS> $LastExitCode
0
PS> $?
True

I’ll now purposefully ping a host that doesn’t resolve a DNS name and will fail. Notice the value of $LastExitCode.

PS> ping googlllll.com

Ping request could not find host googlllll.com. Please check the name and try again.
PS> $LASTEXITCODE
1

終了コードが異なることに気づきましたが、PowerShellは実行可能なファイルの場合にはストリームの違いを理解しません。コンソールに表示されるテキストは白色であり、おなじみの赤いエラーテキストではありません。

次のように、>2>を使用して、ストリームをキャプチャしてファイルにリダイレクトする方法がいくつかあります。これが古い方法です。

PS> ping googllll.com > output.msg 2> output.err
PS> Get-Content .\output.err
PS> Get-Content .\output.msg

Ping request could not find host googllll.com. Please check the name and try again.

ただし、この場合、上記のようにping.exeが終了コード1を返しているにもかかわらず、その出力は依然として標準出力に表示されます。これは一般的です。返されるストリームと終了コードは、完全に開発者によって決まり、さまざまなスタイルで提供されます。

「新しい方法」を選択する場合は、Start-Processを使用し、-RedirectStandardErrorおよび-RedirectStandardOutputパラメータを使用することもできますが、それらは依然としてファイルに書き込まれます。

Start-Processの制限

一般的な出力を返すプロセスを起動することはあまり一般的ではありません。さらに、PowerShellはストリームと終了コードをうまく処理しません。Start-processを使用してping.exeを実行し、結果を追跡したい場合は、実行するたびにこのような処理をする必要があります。

PS> Start-Process -FilePath -NoNewWindow ping -ArgumentList 'google.com' -RedirectStandardOutput output.txt -RedirectStandardError err.txt
PS> $LastExitCode
PS> Get-Content -Path output.txt
PS> Get-Content -Path err.txt

I still wouldn’t get my error text either!

これを修正しましょう。PowerShellギャラリーから作成したInvoke-Processという小さな関数をダウンロードしてください。

PS> Install-Script 'Invoke-Process'
PS> . Invoke-Process.ps1

有効なホストを使用してpingを実行し、何が起こるかを確認してください。

Invoke-Process -FilePath ping -ArgumentList ‘google.com’

同じ出力を得ることに注目してください。これは良いです!

今、無効なホストを使用してpingを実行します。

Invoke-Process -FilePath ping -ArgumentList ‘googlddde.com’

さて、何かがうまくいかなかった場合に予想される通り、典型的な赤いエラーテキストが表示されることに注目してください。 Invoke-Process は、stdout、stderr、および終了コードをキャプチャするために Start-Process を使用し、その出力をどのストリームに送るかに応じて送信します。これが正しい方法です!

PowerShellが実行可能ファイルからのストリームと終了コードを扱うことをより簡単にする方法は残念ながらありませんでしたが、今ではオープンソース化されたので、それを追加することができるようになりました!

Source:
https://adamtheautomator.com/start-process/