Powershell 将 stderr 和 stdout 重定向到两个不同的地方

Powershell redirect stderr and stdout to two different places

如何重定向

我看过的东西:

>>2>> 仅重定向到文件 .
-RedirectStandardOutput-RedirectStandardError 仅再次重定向到文件。
| Out-File 无法重定向标准错误。
| Tee-Object 同样的问题。

加入 stdoutstderr 输出流就像 PetSerAl 评论的那样工作,尽管语法不是最直观的。

2>&1 的奇怪语法意味着 stderr(流 2)将被添加到 stdout(流 1)中。因为这实际上不是您想要的,请尝试将 MS 页面中的另一个示例改编为 Powershell:

Or, you can redirect the output to one place, and the errors to another.

dir file.xxx > output.msg 2> output.err

因此,

$ret = myCommand 2> errors.log

应该在日志文件中发送错误,在 $ret 变量中发送非错误。

about_Redirection MSDN 文章中的全面解释。

A Minimal, Complete, and Verifiable examplestdout 到管道 ):

PS D:\PShell> -1,5,0,2| ForEach-Object { 15/$_ } 2>"$env:temp\err.txt" | Write-Output
-15
3
7.5

PS D:\PShell> Get-Content "$env:temp\err.txt"
Attempted to divide by zero.
At line:1 char:28
+ -1,5,0,2| ForEach-Object { 15/$_ } 2>"$env:temp\err.txt" | Write-Outpu ...
+                            ~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException


PS D:\PShell> 

另一个例子(stdout反对):

PS D:\PShell> $x = -1,5,0,2| ForEach-Object { 15/$_} 2>"$env:temp\err.txt"

PS D:\PShell> $x
-15
3
7.5
cls
function GetAnsVal {
    param([Parameter(Mandatory=$true, ValueFromPipeline=$true)][System.Object[]][AllowEmptyString()]$Output,
          [Parameter(Mandatory=$false, ValueFromPipeline=$true)][System.String]$firstEncNew="UTF-8",
          [Parameter(Mandatory=$false, ValueFromPipeline=$true)][System.String]$secondEncNew="CP866"
    )
    function ConvertTo-Encoding ([string]$From, [string]$To){#"UTF-8" "CP866" "ASCII" "windows-1251"
        Begin{
            $encFrom = [System.Text.Encoding]::GetEncoding($from)
            $encTo = [System.Text.Encoding]::GetEncoding($to)
        }
        Process{
            $Text=($_).ToString()
            $bytes = $encTo.GetBytes($Text)
            $bytes = [System.Text.Encoding]::Convert($encFrom, $encTo, $bytes)
            $encTo.GetString($bytes)
        }
    }
    $all = New-Object System.Collections.Generic.List[System.Object];
    $exception = New-Object System.Collections.Generic.List[System.Object];
    $stderr = New-Object System.Collections.Generic.List[System.Object];
    $stdout = New-Object System.Collections.Generic.List[System.Object]
    $i = 0;$Output | % {
        if ($_ -ne $null){
            if ($_.GetType().FullName -ne 'System.Management.Automation.ErrorRecord'){
                if ($_.Exception.message -ne $null){$Temp=$_.Exception.message | ConvertTo-Encoding $firstEncNew $secondEncNew;$all.Add($Temp);$exception.Add($Temp)}
                elseif ($_ -ne $null){$Temp=$_ | ConvertTo-Encoding $firstEncNew $secondEncNew;$all.Add($Temp);$stdout.Add($Temp)}
            } else {
                #if (MyNonTerminatingError.Exception is AccessDeniedException)
                $Temp=$_.Exception.message | ConvertTo-Encoding $firstEncNew $secondEncNew;
                $all.Add($Temp);$stderr.Add($Temp)
            }   
         }
    $i++
    }
    [hashtable]$return = @{}
    $return.Meta0=$all;$return.Meta1=$exception;$return.Meta2=$stderr;$return.Meta3=$stdout;
    return $return
}
Add-Type -AssemblyName System.Windows.Forms;
& C:\Windows\System32\curl.exe 'api.ipify.org/?format=plain' 2>&1 | set-variable Output;
$r = & GetAnsVal $Output
$Meta2=""
foreach ($el in $r.Meta2){
    $Meta2+=$el
}
$Meta2=($Meta2 -split "[`r`n]") -join "`n"
$Meta2=($Meta2 -split "[`n]{2,}") -join "`n"
[Console]::Write("stderr:`n");
[Console]::Write($Meta2);
[Console]::Write("`n");
$Meta3=""
foreach ($el in $r.Meta3){
    $Meta3+=$el
}
$Meta3=($Meta3 -split "[`r`n]") -join "`n"
$Meta3=($Meta3 -split "[`n]{2,}") -join "`n"
[Console]::Write("stdout:`n");
[Console]::Write($Meta3);
[Console]::Write("`n");