如何在 powershell 脚本中显示 xcopy 的输出 运行

How to show output of xcopy in powershell script while it's running

我有这个 powershell 脚本,我想将文件从我们的生产服务器复制到我们的测试服务器,并且只复制更新或更改的文件。 脚本本身具有更多功能,但此复制是其中之一。 为此,我正在使用以下 XCopy 命令:

  ... some other scriptingcode
  try {
      xCopy $source $destination /f /e /r /k /y /d
  }
  catch {
      Write-Host "issue: $PSItem" -ForegroundColor Yellow -BackgroundColor Red
  }
  ... some more other scriptingcode

当我 运行 直接从 powershell 提示符下执行 XCopy 命令时,它显示它的进度,正在复制文件,虽然它是 运行ning,但是当我 运行 它来自脚本本身,它不输出任何内容。在最后添加 -Verbose 只会给我一个脚本错误。

我试过以下方法:

  $output = $(xCopy $source $destination /f /e /r /k /y /d) -join "`r\`n"
  Write-Host $output

但这只会在它已经被复制后给我输出,当没有复制 1 或 2 个文件时这不是问题,但当你有 100 或 1.000 个文件要复制时就没有真正帮助了。仅供参考,-join "'r'n" 是因为当输出多行时输出是一个数组,所以我不得不将这些行连接成一个字符串,可以通过 Write-Host.

显示

我已经尝试 google 寻求解决方案,但我想我的 google 技能还不足以让我解决这个问题。 那么,有没有办法让 XCopy 输出,就像直接从 powershell 命令行一样?

这是在这里使用子表达式运算符 $() 的副作用(请注意,使用组表达式运算符 () 也会产生相同的效果)。发生这种情况的原因是因为 $()() 的功能类似于数学中的括号,并且运算顺序(在计算中 precedence 的技术顺序)决定了内部表达式是在外部表达式之前计算出来。因此,括号内的任何内容都必须先完成 ,然后才能被外部代码处理。

在您的例子中,“外部代码”是向控制台显示信息的代码,因此,为什么内部命令必须在显示之前完成。


如果您以后不需要评估 $output,最简单和最高效的方法是将命令输出直接通过管道传输到 Out-Host(您可能还想重定向错误输出以及):

# 2>&1 will redirect the error stream (external command STDERR gets written here)
# to the success stream (external command STDOUT gets written here)
# The success stream is what gets passed down the pipeline
xCopy $source $destination /f /e /r /k /y /d 2>&1 | Out-Host

但是,如果需要同时将$output赋值给一个变量进行进一步处理,参见 involving the clever use of ForEach-Object. There are some edge caveats to their approach but does allow you to display the output and either stage the output in another variable for later processing or process the lines of output as they are read in. 也展示了如何使用Tee-Object同时输出到一个变量和成功流。

对于 xcopy,您既可以输出到主机,也可以使用 foreach:

捕获输出
try {
  $output = xcopy $src $dst /f /e /r /k /y /d | 
    Foreach {
      # write line to console
      $_ | Write-Host -ForegroundColor Yellow -BackgroundColor Red
    
      # add line to $output
      $_
    }
}

请注意,xcopy 不是 powershell 命令,因此它无法执行某些操作,例如使用默认的 -verbose-ErrorAction 标志。它也不会抛出终止错误,因此除非您正确设置 $ErrorActionPreference,否则 Try/Catch 将不起作用。

不应该发现你的脚本有问题,因为默认情况下 PowerShell 传递 stdout 和 stderr 输出从外部程序,例如xcopy.exe 直接到主机(控制台),不干扰输出或它的时机 - 无论您是以交互方式还是从脚本执行程序。

类似地,如果您通过管道发送 stdout(成功)输出 ,PowerShell streams 它,即发送每个输出行 被接收时 (可以想象,当不打印到控制台时,给定的程序本身可能会在发出行之前缓冲行,但这似乎不是xcopy.exe).

最简单的方法是流式传输方式将外部程序的输出打印到控制台,同时还捕获其输出 是使用 Tee-Object cmdlet:

# Prints the output to the console as it is being received
# while also capturing it in variable $output
xcopy $source $destination /f /e /r /k /y /d | Tee-Object -Variable output

之后,$output 将 stdout 输出包含为 行数组 (或单个字符串,如果发生只是一个输出行)。

要同时捕获 stderr 输出,附加 redirection 2>&1 to the external-program call; stderr lines are captured as System.Management.Automation.ErrorRecord 个实例,就好像它们是 PowerShell错误;之后将所有数组元素转换为 strings,使用 $output = [string[]] $output.

字符编码说明:当PowerShell参与处理外部程序输出时(在管道中,捕获到变量中,保存到文件中),输出为总是首先解码为 .NET 字符串,基于存储在 [Console]::OutputEncoding 中的编码,在某些情况下可能需要修改以匹配给定外部程序使用的特定编码 - 请参阅 获取更多信息。

如果您想对从外部程序接收的行执行转换或应用额外的格式,请考虑 based on ForEach-Object.