标准输出和错误输出重定向并追加到同一个日志文件中

65

我需要将多个进程的标准输出和错误日志收集到一个单独的日志文件中。

因此,每个输出必须追加到该日志文件中。

我希望使用类似于以下行的方式调用所有作业:

$p=start-process myjob.bat -redirectstandardoutput $logfile -redirecterroroutput $logfile -wait

我应该把要添加的信息放在哪里?

4个回答

97
为了向文件追加内容,您需要使用稍微不同的方法。您仍然可以将单个进程的标准错误和标准输出重定向到文件中,但是要将其追加到文件中,您需要执行以下操作之一:
  1. 读取由 Start-Process 创建的 stdout/stderr 文件内容
  2. 不使用 Start-Process 并使用 调用运算符,&
  3. 不使用 Start-Process 并使用 .NET 对象启动进程

第一种方法看起来像这样:

$myLog = "C:\File.log"
$stdErrLog = "C:\stderr.log"
$stdOutLog = "C:\stdout.log"
Start-Process -File myjob.bat -RedirectStandardOutput $stdOutLog -RedirectStandardError $stdErrLog -wait
Get-Content $stdErrLog, $stdOutLog | Out-File $myLog -Append

第二种方法如下所示:
& myjob.bat 2>&1 >> C:\MyLog.txt

或者这样:

& myjob.bat 2>&1 | Out-File C:\MyLog.txt -Append

第三种方法:
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "myjob.bat"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = ""
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$output = $p.StandardOutput.ReadToEnd()
$output += $p.StandardError.ReadToEnd()
$output | Out-File $myLog -Append

它适用于PowerShell远程吗?我已经测试过了,我认为只有第二种方式适用于PS Remoting。 - Kiquenet
5
第三种方法在某些情况下会导致死锁,阅读MSDN或这里的讨论https://dev59.com/aGoy5IYBdhLWcg3wHqeB#8762068。 - mloskot
太好了!据我所知,2>&1 | Out-File $file -Append -Encoding UTF8 是唯一同时重定向标准输出和标准错误输出并使用UTF-8输出的方法。 - marsze

24

与Unix shell类似,PowerShell支持使用>重定向输出流,并支持Unix中的大多数变种,包括2>&1。尽管有点奇怪,顺序不重要 - 2>&1 > file 的效果与正常的> file 2>&1相同。

与现代Unix shell大多数情况一样,PowerShell还具有将标准错误和标准输出重定向到同一设备的快捷方式,虽然与其他遵循Unix惯例的缩写不同,此缩写使用了一个新的符号,书写方式如下:*>

因此,您的实现可能是:

& myjob.bat *>> $logfile

23

Andy给了我一些好的指导,但我想用更清晰的方式来做。更不用说使用2>&1 >>方法时,PowerShell向我抱怨日志文件被另一个进程访问,也就是stderr和stdout都试图锁定该文件进行访问吧。所以这就是我解决它的方法。

首先,让我们生成一个漂亮的文件名,但这只是为了追求严谨:

$name = "sync_common"
$currdate = get-date -f yyyy-MM-dd
$logfile = "c:\scripts\$name\log\$name-$currdate.txt"

这里就是诀窍的开始:

start-transcript -append -path $logfile

write-output "starting sync"
robocopy /mir /copyall S:\common \\10.0.0.2\common 2>&1 | Write-Output
some_other.exe /exeparams 2>&1 | Write-Output
...
write-output "ending sync"

stop-transcript

使用 start-transcriptstop-transcript,您可以将PowerShell命令的所有输出重定向到单个文件,但是它在处理外部命令时无法正常工作。因此,让我们只将这些命令的所有输出重定向到PS的标准输出,然后交给记录器去处理。

实际上,我不知道为什么微软的工程师说他们还没有修复这个问题,原因是“成本高且涉及技术复杂性”,当事实上可以用如此简单的方法绕过它。

无论哪种方式,对于我来说,每次都使用 start-process 运行每个命令都会产生很多混乱,但是通过这种方法,您只需要将 2>&1 | Write-Output 代码附加到运行外部命令的每一行即可。


因为它无法确定,它完全取决于您所调用的程序的功能。在这种情况下,“robocopy”是有效的,但并非所有程序都适用。 - Luiz Felipe

0

也许这种方法不太优雅,但以下代码也可以工作。我怀疑在异步情况下这不是一个好的解决方案。

$p = Start-Process myjob.bat -redirectstandardoutput $logtempfile -redirecterroroutput $logtempfile -wait
add-content $logfile (get-content $logtempfile)

5
这实际上行不通,因为PowerShell不允许你将"redirectstandardouput"和"redirecterroroutput"重定向到同一个文件中。当我尝试这样做时,出现了错误提示:"Start-Process:此命令无法运行,因为“RedirectStandardOutput”和“RedirectStandardError”相同。请提供不同的输入并重新运行您的命令。" - Bob
我认为海报的意思是在-redirectstandardoutput中使用$logFile。该exe将写入两个文件,然后第二行将一个文件附加到另一个文件。那样可以工作。但是,它不会整理行;只是附加。通常,exe仅在运行结束时才写入err,因此通常可以正常工作。但不适用于一般情况。 - steve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接