【问题标题】:How to show output of xcopy in powershell script while it's running如何在运行时在 powershell 脚本中显示 xcopy 的输出
【发布时间】:2021-12-11 04:01:01
【问题描述】:

我有这个 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 命令时,它会在运行时显示它的进度、正在复制的文件,但是当我从脚本本身运行它时,它不会输出任何内容。只是在最后添加-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 显示。

我试图用谷歌搜索解决方案,但我想我的谷歌技能不足以让我得到解决这个问题的任何结果。 那么,有没有办法让XCopy 输出,就像直接从 powershell 命令行一样?

【问题讨论】:

  • 通常xcopy 从控制台或脚本调用时的行为不会有所不同。您是否重定向/管道输出脚本?在这种情况下,输出可能会延迟,因为重定向时许多本机可执行文件会缓冲输出。
  • 顺便说一句:try / catch 对调用外部程序(如xcopy.exe)没有影响。检查 $LASTEXITCODE 是否包含非零值以推断失败。

标签: powershell xcopy


【解决方案1】:

这是在此处使用子表达式运算符 $() 的副作用(请注意,使用组表达式运算符 () 也会获得相同的效果)。发生这种情况的原因是因为 $()() 在数学中的作用类似于括号,并且运算顺序(计算中的技术顺序 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 分配给一个变量以进行进一步处理,请参阅@Cpt.Whale's solution,其中涉及巧妙地使用ForEach-Object。他们的方法有一些边缘警告,但确实允许您显示输出并将输出暂存到另一个变量中以供以后处理或在读入时处理输出行。@mklement0's answer 还显示了如何使用Tee-Object同时输出到变量和成功流。

【讨论】:

  • thanx,Out-Host 也是一个很好的解决方案,因为目前我不需要输出供以后使用。你和@Cpt.Whale 的解决方案都不是我想过的,Out-Host 也从未出现在我的谷歌搜索结果中,哈哈..
  • 大多数时候你不想想使用Out-Host,因为使用它也有一定的影响。对于初学者来说,与写入信息流Write-Host不同,Out-Host直接输出到控制台,因此除了使用Start-Transcript进行日志记录外,任何重定向都是不可能的。但在这种情况下,你应该没问题。
【解决方案2】:

对于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 将不起作用。

【讨论】:

  • 该死的,这是对 ForEach-Object 的非常聪明的使用:D
  • 需要注意的一点是,如果这种方法用于 PowerShell 对象而不是外部命令的字符串/输出; Write-Host 将首先将 ToString() 正确的对象,而 Out-Host 不会。如果不需要彩色输出,则可能需要Out-Host,否则在播放非字符串对象时需要进行额外处理以获得合适的输出。
  • 另外,ErrorActionAFAIK 从不触发非零退出代码。您必须检查自己和 throw/Write-Error
  • 正如 Bender 所说,这确实是解决我问题的好方法。这正是我所需要的,尽管目前 Bender the Greatest 也是一个很好的解决方案,因为我不需要输出进行进一步分析。现在我将使用 Bender 的解决方案,但我会在同一位置将您的解决方案注释掉。 Try/Catch 是我尝试使用 Copy-Item 时留下的东西。
  • @BendertheGreatest :P - 我只对xcopy 进行了快速测试,但设置错误首选项确实可以让您在出现错误时运行 catch 块。还是您的意思只是-ErrorAction 标志?我想我可以选择一个不那么令人困惑的例子,但这只是为了帮助表明 xcopy 不会像豪华的 cmdlet 那样行事。
【解决方案3】:

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

同样,如果您通过管道发送标准输出(成功)输出,PowerShell 它,即发送每个输出行原样收到(可以想象,一个给定的程序在不打印到控制台时可能会在发出它们之前自己缓冲行,但xcopy.exe 似乎不是这种情况)。

流式传输方式将外部程序的输出打印到控制台同时捕获其输出的最简单方法是使用@987654321 @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 附加到外部程序调用中; stderr 行被捕获为 System.Management.Automation.ErrorRecord 实例,就好像它们是 PowerShell 错误一样;之后要将所有数组元素转换为字符串,请使用$output = [string[]] $output

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

如果您想对从外部程序接收的行执行转换或应用额外的格式,请考虑基于ForEach-ObjectCpt.Whale's helpful solution

【讨论】:

  • 我不知道Tee-Object 让你指定一个变量,我以为它只拆分到文件。直到!
  • 我尝试了所提供的Tee-Object,但它没有直接在屏幕上显示任何内容,只有当我在它之后有一个断点并且在它到达断点时我停止了脚本时。但是如果我删除断点,什么都不会显示。我确实直接从 Windows PowerShell ISE (x86) 运行脚本,所以可能存在不直接显示输出的问题。
  • 很高兴听到这个消息,@BendertheGreatest。另一种选择是(尽管它没有提供太多优势,除非在 PS Core 中,如果您想将与 2>&1 合并的 stderr 行直接捕获为 strings) :xcopy $source $destination /f /e /r /k /y /d | Out-String -Stream -OutVariable output
  • @SuperDre,您的设置一定有一些不寻常的地方 - 我看不到问题,无论是在 ISE 的 32 位还是 64 位版本中。顺便说一句:ISE 已经过时,我建议迁移到 Visual Studio Code(请参阅下一条评论)。您可以尝试附加| Out-Host 看看它是否对您的情况有帮助,但我对您的症状没有任何解释:xcopy $source $destination /f /e /r /k /y /d | Tee-Object -Variable output | Out-Host
  • 顺便说一句:PowerShell ISE 是 no longer actively developedthere are reasons not to use it(底部),特别是无法运行 PowerShell (Core) 6+。提供最佳 PowerShell 开发体验的积极开发的跨平台编辑器是 Visual Studio Code 及其 PowerShell extension
猜你喜欢
  • 1970-01-01
  • 2014-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-19
  • 2020-09-07
  • 2017-08-14
  • 1970-01-01
相关资源
最近更新 更多