PowerShellの外部プロセスから変数に出力をキャプチャするにはどうすればよいですか?


158

外部プロセスを実行し、そのコマンド出力をPowerShellの変数にキャプチャします。私は現在これを使用しています:

$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait

コマンドが実行されていることを確認しましたが、出力を変数にキャプチャする必要があります。これは、ファイルにリダイレクトするだけなので、-RedirectOutputを使用できないことを意味します。


3
何よりもStart-Processまず:(定義により)コンソールアプリケーションを同期的に実行するために使用しないでください-他のシェルのように、それらを直接呼び出すだけです。ウィットに:netdom /verify $pc /domain:hosp.uhhg.org。そうすることで、アプリケーションが呼び出し側コンソールの標準ストリームに接続されたままになり、単純な割り当てでその出力をキャプチャでき$output = netdom ...ます。以下に示す回答のほとんどStart-Processは、直接実行を支持することを暗黙のうちに無視しています。
mklement0 2018

@ mklement0(ただし、-Credentialパラメータを使用する場合を除く)
CJBS

@CJBSはい、別のユーザーIDで実行するにStart-Processは、の使用が必須ですが、それが必要になるのは(別のウィンドウでコマンドを実行する場合)です。そして、その場合、避けられない制限に注意する必要があります。出力をキャプチャする機能は、インターリーブされていない テキストとして、およびを介してファイルに保存さます-RedirectStandardOutput-RedirectStandardError
mklement0

回答:


161

やってみました:

$OutputVariable = (Shell command) | Out-String


「=」を使用して変数に割り当てようとしましたが、出力を最初にOut-Stringにパイプしようとしませんでした。やってみます。
アダムバートラム

10
ここで何が起こっているのか理解できず、機能させることができません。「シェル」はPowerShellのキーワードですか?したがって、実際にはStart-Processコマンドレットを使用していませんか?具体的な例を挙げてください(つまり、「シェル」や「コマンド」を実際の例に置き換えてください)。
deadlydog

@deadlydog Shell Command実行したいものに置き換えてください。とても簡単です。
JNK 2013

1
@stej、あなたは正しい。私は主に、コメントのコードが回答のコードとは異なる機能を持っていることを明確にしていました。私のような初心者は、このような振る舞いの微妙な違いによって気が散ることがあります!
サム・

1
@Atique同じ問題に遭遇しました。たとえば、-i出力ファイルを指定せずにオプションを使用した場合、ffmpegがstdoutではなくstderrに書き込むことがあることがわかります。2>&1他の回答のいくつかで説明されているようにを使用して出力をリダイレクトすることがソリューションです。
jmbpiano 2017

159

注:質問のコマンドではを使用しているためStart-Process、ターゲットプログラムの出力が直接キャプチャされません。通常は、コンソールアプリケーションを同期に実行するために使用しないStart-Processください。他のシェルと同様に、直接呼び出すだけです。そうすることで、アプリケーションが呼び出しコンソールの標準ストリームに接続されたままになり、$output = netdom ...以下に詳述するように、単純な割り当てによって出力をキャプチャできます。

基本的に外部ユーティリティからの出力のキャプチャは、PowerShellネイティブコマンドと同じように機能します外部ツールの実行方法について復習したい場合があります)。

$cmdOutput = <command>   # captures the command's success stream / stdout

$cmdOutputは、複数の出力オブジェクトを生成する場合にオブジェクトの配列を受け取ります。これは、外部プログラムの場合、プログラムの出力を含む文字列配列を意味します。常に単一の、 場合によっては複数行の文字列を受け取りたい場合は、<command>
$cmdOutput
$cmdOutput = <command> | Out-String


キャプチャ変数に出力し、画面に印刷

<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed

または、コマンドレットまたは高度な関数の場合<command>は、共通パラメーター/を使用できます。
-OutVariable-ov

<command> -OutVariable cmdOutput   # cmdlets and advanced functions only

なおとともに-OutVariable、他のシナリオとは異なり、 $cmdOutputある常に収集だけしても、1オブジェクトが出力されます。具体的には、配列のような[System.Collections.ArrayList]型のインスタンスが返されます。この矛盾については、GitHubの問題
ご覧ください。


以下からの出力をキャプチャするには、複数のコマンド、使用のいずれかの部分式($(...))または(スクリプトブロックを呼び出す{ ... })を持ちます&.

$cmdOutput = $(<command>; ...)  # subexpression

$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.

$cmdOutput = . {<command>; ...} # script block with . - no child scope

なお、接頭辞に一般的な必要性&(コールオペレータ)、名前/パスされた個々のコマンドを引用し -例えば、$cmdOutput = & 'netdom.exe' ...-それ自体が外部プログラムに関連していない(それが均等にPowerShellスクリプトに適用されます)が、ある構文要件:PowerShellのは、デフォルトでは式モードで引用符付きの文字列で始まるステートメントを解析しますが、コマンド(コマンドレット、外部プログラム、関数、エイリアス)を呼び出すには引数モードが必要です。&

$(...)& { ... }/ の主な違い. { ... }は、前者全体を返す前にすべての入力をメモリ収集するのに対し、後者は1つずつパイプライン処理に適した出力をストリーミングすることです。


リダイレクトも基本的には同じように機能します(ただし、以下の警告を参照してください)。

$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)

ただし、外部コマンドの場合、以下は期待どおりに機能する可能性が高くなります。

$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.

外部プログラムに固有の考慮事項:

  • 外部プログラムは、PowerShellの型システムの外部で動作するため、成功ストリーム(stdout)を介してのみ文字列返します

  • 出力に複数の行が含まれている場合、PowerShellはデフォルトでそれを文字列の配列分割します。より正確には、出力行は[System.Object[]]、要素が文字列([System.String])であるタイプの配列に格納されます。

  • あなたがいる場合、出力になりたい、単一の、潜在的に複数行の文字列をパイプへOut-String
    $cmdOutput = <command> | Out-String

  • stderrをでstdoutにリダイレクトし2>&1、成功ストリームの一部としてもキャプチャできるようにするには、注意が必要です。

    • 作るために2>&1マージstdoutとstderrを元に聞かせてcmd.exeリダイレクトを処理し、次のイディオムを使用して、:
      $cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
      $cmdOutput = cmd /c <command> '2>&1' | Out-String # single string

      • cmd /ccmd.exeコマンド<command>を使用して呼び出し、終了後<command>に終了します。
      • 周りの単一引用符に注意してください2>&1。これにより、リダイレクトがcmd.exePowerShellによって解釈されるのではなく、確実に渡されます。
      • 関与cmd.exeとは PowerShell独自の要件に加えて、文字のエスケープと環境変数の拡張に関するルールがデフォルトで機能することを意味します。PS v3 +では、特別なパラメーター--%(いわゆる解析停止記号)を使用して、PowerShellによる残りのパラメーターの解釈をオフにすることができます。ただし、cmd.exeなどの-スタイルの環境変数参照は除きます%PATH%

      • このアプローチでは、ソースでstdoutとstderr マージしているため、PowerShellでstdoutから発生した行とstderrから発生した行を区別できないことに注意してください。この区別が必要な場合は、PowerShell独自の2>&1リダイレクトを使用してください-以下を参照してください。

    • PowerShellの 2>&1リダイレクトを使用して、どのストリームがどのストリームからのものかを確認します

      • stderr出力は文字列ではなくエラーレコード[System.Management.Automation.ErrorRecord])としてキャプチャされるため、出力配列には文字列(各文字列はstdout行を表す)とエラーレコード(各レコードはstderr行を表す)の混合が含まれる場合があります。からの要求に応じて2>&1、文字列とエラーレコードの両方がPowerShellの成功出力ストリームを介して受信されることに注意してください。

      • コンソールでは、エラーレコードはで印刷され、最初のレコードはデフォルトで、コマンドレットの非終了エラーが表示するのと同じ形式で複数行の表示を生成します。後続のエラーレコードも赤で印刷されますが、エラーメッセージ1行でのみ印刷されます。

      • コンソールに出力する場合、通常、文字列出力配列の最初に来て、その後にエラーレコードが続きます(少なくとも「同時に」出力されるstdout / stderr行のバッチの間)が、幸い、出力キャプチャすると、適切にインターリーブされ、なしで得られるのと同じ出力順序を使用します2>&1。つまりコンソールに出力する場合、キャプチャされた出力は、stdoutおよびstderr行が外部コマンドによって生成された順序を反映していません。

      • あなたがいる場合における全体の出力キャプチャする単一の文字列をOut-StringPowerShellは追加されます余分な行をエラーレコードの文字列表現は、このような場所(などの追加情報が含まれているので、At line:...)およびカテゴリを(+ CategoryInfo ...); 奇妙なことに、これは最初のエラーレコードにのみ適用されます。

        • この問題を回避するには、適用.ToString()の代わりに、配管の各出力オブジェクトのメソッドをOut-String
          $cmdOutput = <command> 2>&1 | % { $_.ToString() };
          PS v3 +では、次のように簡略化できます
          $cmdOutput = <command> 2>&1 | % ToString
          (おまけとして、出力がキャプチャされない場合、コンソールに出力する場合でも、適切にインターリーブされた出力が生成されます)。
      • また、フィルタエラーが記録されとしてPowerShellののエラーストリームに送信Write-Error(出力がキャプチャされていない場合はボーナスとして、これは適切にコンソールに印刷しても、インターリーブ出力を生成します):

$cmdOutput = <command> 2>&1 | ForEach-Object {
  if ($_ -is [System.Management.Automation.ErrorRecord]) {
    Write-Error $_
  } else {
    $_
  }
}

これは、実行可能パスとその引数を取得して文字列に放り込み、それを私の<コマンド>として処理した結果、最終的には機能しました。
Dan

2
@Dan:PowerShellの自身が解釈した場合<command>、あなたはしてはならない単一の文字列で実行可能ファイルと引数を組み合わせます。cmd /cあなたを介した呼び出しでは、そうすることができ、それが理にかなっているかどうかは状況に依存します。あなたはどのシナリオを参照していますか、そして最小限の例を挙げられますか?
mklement0

機能:$ command = "c:\ mycommand.exe" + $ Args ..... $ output = cmd / c $ command '2>&1'
Dan

1
@Dan:はい、動作しますが、+演算子を使用して中間変数や文字列を明示的に作成する必要はありません。以下も機能します。cmd /c c:\mycommand.exe $Args '2>&1'-PowerShellは、要素を$Argsスペースで区切られた文字列として渡す処理を行います。この場合、スプラッティングと呼ばれる機能です。
mklement0

最後に、PS6.1 +で機能する適切な答えです。ソースの秘密は確かに'2>&1'一部であり()、多くのスクリプトが行う傾向があるように囲まれていない。
not2qubit

24

エラー出力もリダイレクトしたい場合は、次のようにする必要があります。

$cmdOutput = command 2>&1

または、プログラム名にスペースが含まれている場合:

$cmdOutput = & "command with spaces" 2>&1

4
2>&1の意味?'実行コマンドは2と呼ばれ、その出力を1と呼ばれる実行コマンドに入れますか?
Richard

7
これは、「標準エラー出力(ファイル記述子2)を、標準出力(ファイル記述子1)と同じ場所にリダイレクトする」ことを意味します。基本的に、通常のメッセージとエラーメッセージを同じ場所にリダイレクトします(この場合、コンソール、stdoutがファイルのように他の場所にリダイレクトされない場合)。
Giovanni Tirloni、2014年

11

またはこれを試してください。出力を変数$ scriptOutputにキャプチャします。

& "netdom.exe" $params | Tee-Object -Variable scriptOutput | Out-Null

$scriptOutput

7
-1、不必要に複雑。$scriptOutput = & "netdom.exe" $params
CharlesB 2013

8
ヌルを削除すると、シェルと変数の両方に同時にパイプするのに最適です。
ferventcoder、2014

10

別の実際の例:

$result = & "$env:cust_tls_store\Tools\WDK\x64\devcon.exe" enable $strHwid 2>&1 | Out-String

この例にはパス(環境変数で始まる)が含まれていることに注意してください。引用符はパスとEXEファイルを囲む必要がありますが、パラメーターを囲む必要はないことに注意してください!

注:&コマンドの前の引用符の外の文字を忘れないでください。

エラー出力も収集されます。

この組み合わせが機能するまでに少し時間がかかったので、共有したいと思いました。


8

私は答えを試しましたが、私の場合、生の出力が得られませんでした。代わりに、PowerShell例外に変換されました。

私が得た生の結果:

$rawOutput = (cmd /c <command> 2`>`&1)

2

私は以下を機能させました:

$Command1="C:\\ProgramData\Amazon\Tools\ebsnvme-id.exe"
$result = & invoke-Expression $Command1 | Out-String

$ resultは必要なものを提供します


1

これは私にとってはうまくいきました:

$scriptOutput = (cmd /s /c $FilePath $ArgumentList)

0

コマンドからの出力をキャプチャするだけの場合は、これでうまくいきます。

システムに変更を加えた[timezoneinfo]::localでも、常に同じ情報を生成するので、システム時間の変更に使用します。これは、タイムゾーンの変更を検証してログに記録できる唯一の方法です。

$NewTime = (powershell.exe -command [timezoneinfo]::local)
$NewTime | Tee-Object -FilePath $strLFpath\$strLFName -Append

つまり、システム変数を再読み込みするには、新しいPowerShellセッションを開く必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.