【问题标题】:Start-Process, Invoke-Command or?启动进程,调用命令还是?
【发布时间】:2021-08-10 13:11:40
【问题描述】:

使用该程序得到了您的支持或 GYB。我运行以下命令

Start-Process -FilePath 'C:\Gyb\gyb.exe' -ArgumentList @("--email <Email Address>", "--action backup", "--local-folder $GYBfolder", "--service-account", "--batch-size 4") -Wait

问题是当这个过程完成后我的脚本没有完成。

$GYBfolder = $GYBfolder.Replace('"', "")
$output = [PSCustomObject]@{
    Name   = $SourceGYB
    Folder = $GYBfolder
}

$filename = "C:\reports\" + $SourceGYB.Split("@")[0] + "_Backup.csv"

$output | Export-Csv $filename -NoTypeInformation | Format-Table text-align=left -AutoSize
Return $filename

由于某种原因,脚本在返回之前停止。 我很想知道我是否应该使用不同的命令来运行 GYB? 关于脚本为什么不处理返回的任何想法?

【问题讨论】:

  • 这里可能没有理由使用Start-Process。你真的应该只在你想要异步执行或者绝对需要它输出的属性之一或它提供的参数时才使用它。
  • @AdminOfThings 我应该将其更改为 invoke-command 还是?
  • 我没有使用过这个命令,但是如果你的程序路径在引号中,你应该可以直接用&amp;(调用)操作符来调用它。如果您将参数原样复制到变量中,您将拥有一个数组(例如 $ArgArray)。你可能会运行类似&amp; 'c:\gyb\gyb.exe' $ArgArray的东西。
  • @AdminOfThings 不起作用,我将失去等待。 gyb.exe: error: unrecognized arguments: --email $SourceGYB --action backup --local-folder $GYBfolder --batch-size 4
  • 不要在本地使用Invoke-Command。使用 call &amp; 运算符代替(或省略,它通常是可选的)来执行本地外部命令。虽然您想等待该过程完成,但您在这里使用Start-Process 是合适的。

标签: powershell


【解决方案1】:

cmets 中有大量信息,但让我尝试系统概述

  • 同步执行外部控制台应用程序并捕获它们的输出,直接调用它们C:\Gyb\gyb.exe ...&amp; 'C:\Gyb\gyb.exe' ...),不要使用Start-Process - 请参阅this answer

    • 只有gyb.exe 是一个GUI 应用程序,您才需要**Start-Process -Wait 才能同步执行它**。

      • 一个简单但不明显的快捷方式是将调用通过管道传递给另一个命令,例如Out-Null,这也强制PowerShell等待(例如gyb.exe | Out-Null) - 见下文。
    • Start-Process 合适时,传递所有参数的最可靠方法是作为单个 字符串编码所有参数,并使用适当的嵌入 "..." 引用,如需要;这是不幸的,但作为长期存在的错误的解决方法是必需的:请参阅this answer

  • Invoke-Command 的主要目的是远程调用命令;虽然它可以在本地使用,但很少有充分的理由这样做,因为&amp;call operator 更加简洁和高效 - 请参阅 this answer

  • 当您使用 数组 将参数传递给 外部应用程序 时,每个元素必须只包含 一个 参数,其中参数 名称 和它们的 被认为是不同的参数;例如,您必须使用 @(--'action', 'backup', ...) 而不是
    @('--action backup', ...)

因此,请使用以下命令同步运行您的命令

  • 如果gyb.exe控制台应用程序
# Note: Enclosing @(...) is optional
$argList = '--email',  $emailAddress, '--action', 'backup', '--local-folder', $GYBfolder, '--service-account', '--batch-size',  4

# Note: Stdout and stderr output will print to the current console, unless captured.
& 'C:\Gyb\gyb.exe' $argList
  • 如果gyb.exeGUI 应用程序,则需要使用Start-Process -Wait(使用here-string,因为它使嵌入引用更容易):
# Note: A GUI application typically has no stdout or stderr output, and
#       Start-Process never returns the application's *output*, though
#       you can ask to have a *process object* returned with -PassThru.
Start-Process -Wait 'C:\Gyb\gyb.exe' @"
--email $emailAddress --action backup --local-folder "$GYBfolder" --service-account --batch-size 4
@"

上面提到的快捷方式 - 管道到另一个命令以强制等待 GUI 应用程序退出 - 尽管很晦涩,但有两个优点:

  • 可以使用普通的参数传递语法。
  • automatic $LASTEXITCODE variable 设置为外部程序的进程退出代码,Start-Process 不会发生这种情况。虽然 GUI 应用程序很少报告有意义的退出代码,但有些会报告,尤其是 msiexec
# Pipe to | Out-Null to force waiting (argument list shortened).
# $LASTEXITCODE will reflect gyb.exe's exit code.
# Note: In the rare event that the target GUI application explicitly
#       attaches to the caller's console and produces output there,
#       pipe to `Write-Output` instead, and possibly apply 2>&1 to 
#       the application call so as to also capture std*err* output.
& 'C:\Gyb\gyb.exe' --email $emailAddress --action backup | Out-Null

注意:如果上述操作意外同步运行,则暗示gyb.exe 本身启动了另一个异步操作。对此没有通用的解决方案,而特定于应用程序的解决方案将要求您了解应用程序的内部结构,而且这很重要。


关于通过直接/&amp;-based 调用传递参数的注释

  • 将数组按原样传递给外部程序本质上执行splatting隐式,而无需使用@argList[1]支持>。也就是说,它将每个数组元素作为自己的参数传递

  • 相比之下,如果您要将$argList 传递给PowerShell 命令,它将作为单个数组值 参数传递,因此@argList确实有必要将元素作为 separatepositional 参数传递。然而,与 PowerShell 命令一起使用的更典型的 splatting 形式是使用 hashtable,它允许传递 named 参数(参数名称-值对;例如,将值传递给 PowerShell命令的
    -LiteralPath参数:
    $argHash = @{ LiteralPath = $somePath; ... }; Set-Content @argHash


[1] $args@args 在这种情况下基本相同,但奇怪的是,@argList 使用 --%stop-parsing symbol operator,尽管它只在一个字面上指定的参数列表。

【讨论】:

    猜你喜欢
    • 2017-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 2010-10-13
    相关资源
    最近更新 更多