简体   繁体   中英

Invoke-Expression, not all output returned to variable

I'm using some GIT commands in my PowerShell scripts. Most of the time I'm calling the GIT commands via Invoke-Expression so that I, eg

  • can parse the output, or/and
  • forward the out to a logging method.

At some GIT commands I recognized that not all output is returned via Invoke-Expression though the documentation states:

Outputs

PSObject

Returns the output that is generated by the invoked command (the value of the Command parameter).

Here is an example:

> $x = iex "git fetch --all"
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 3), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.

Content of $x :

> $x
Fetching origin
Fetching upstream

So the main information is not returned to $x . I can't imagine that git fetch --all is returning the main information via stderr (wouldn't make sense ...).

I also found this PowerShell question , which is unanswered and the used PowerShell version is 2.

Used PowerShell version:

> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      6.2.0
PSEdition                      Core
GitCommitId                    6.2.0
OS                             Microsoft Windows 10.0.18362
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

How can I force Invoke-Expression to return the whole output?

Thx

As I mentioned in " PowerShell Capture Git Output ", with Git 2.16 (Q1 2018) , you can try and set first:

set GIT_REDIRECT_STDERR=2>&1

Then in your Powershell script, you should get both stdout and stderr outputs,

See also dahlbyk/posh-git issue 109 for a more Powershell-like example:

$env:GIT_REDIRECT_STDERR = '2>&1'

VonC's answer works well with git , specifically, but it's worth discussing a generic solution :


Note: Invoke-Expression should generally be avoided and there is no reason to use it for invocation of external programs : just invoke them directly and assign to a variable:

$capturedStdout = git ... # capture git's stdout output as an array of lines

As has been noted, git outputs status information to stderr , whereas data goes to stdout; a PowerShell variable assignment only captures stdout output . [1]

To capture a combination of stdout and stderr , interleaved, as it would print to the terminal, you can use redirection 2>&1 , as in other shells, to merge the error stream / stderr ( 2 ) into ( >& ) the data output stream (stdout equivalent, 1 - see about_Redirection ):

$combinedOutput = git fetch --all 2>&1 

Caveat : In PowerShell versions up to v7.1, if $ErrorActionPreference = 'Stop' happens to be in effect, the use of 2> unexpectedly triggers a terminating error; this problematic behavior is discussed in GitHub issue #4002 .

There are non-obvious differences to the behavior of other shells, however:

  • The output will be an array of lines , not a single, multi-line string,

    • Note: As of PowerShell 7.2 - external-program output is invariably interpreted as text (strings) - there is no support for raw binary output; see this answer .
  • Lines that originated from stdout are represented as strings , as expected, but lines originating from stderr are actually [System.Management.Automation.ErrorRecord] instances, though they print like strings and on conversion to strings do reproduce the original line, such as when sending the result to an external program.
    This answer shows how to separate the captured lines by stream of origin (assuming stdout and stderr were merged).

  • Being able to capture stderr output separately in a variable is desirable, which isn't supported as of PowerShell 7.2.x, however. Adding future support, along the lines of 2>variable:errs , is the subject of GitHub issue #4332 .

The array-based result can be advantageous for parsing ; eg, to find a line that contains the word unpacking :

PS> $combinedOutput -match 'unpacking'
Unpacking objects: 100% (4/4), done.

Note: If there's a chance that only one line was output, use @($combinedOutput) -match 'unpacking'

If you prefer to receive a single, multi-line string instead:

$combinedOutput = (git fetch --all 2>&1) -join "`n"  # \n (LF); or: [Environment]::NewLine 

If you don't mind a trailing newline as part of the string, you can more simply use Out-String : [2]

$combinedOutput = git fetch --all 2>&1 | Out-String

Caveat : In Windows PowerShell this won't work as expected if stderr lines are present , as they are rendered like PowerShell error records (this problem has been fixed in PowerShell (Core) 6+ ); run cmd /c 'echo data & echo err >&2' 2>&1 | Out-String cmd /c 'echo data & echo err >&2' 2>&1 | Out-String to see the problem. Use the -join "`n" solution to avoid the problem.


Note:

  • As usual, irrespective of what redirections you use, determining whether an external-program call succeeded or failed should be based only on its exit code , reflected in PowerShell's automatic $LASTEXITCODE variable : By convention (which most, but not all programs observe), 0 indicates success and and any nonzero value failure (a notable exception is robocopy which uses several nonzero exit codes to communicate additional information in the success case).

[1] For comprehensive information on capturing output from external programs in PowerShell, see this answer .

[2] This problematic Out-String behavior is discussed in GitHub issue #14444 .

试试这个(没有iex)

$x=git fetch --all

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM