如何告诉PowerShell在开始下一个命令之前要等待每个命令结束?


211

我有一个PowerShell 1.0脚本只能打开一堆应用程序。第一个是虚拟机,其他是开发应用程序。我希望虚拟机在打开其余应用程序之前完成引导。

用bash我只能说 "cmd1 && cmd2"

这就是我所拥有的...

C:\Applications\VirtualBox\vboxmanage startvm superdooper
    &"C:\Applications\NetBeans 6.5\bin\netbeans.exe"

Answers:


366

通常,对于内部命令,PowerShell会在启动下一个命令之前等待。此规则的一个例外是基于Windows子系统的外部EXE。第一个技巧是流水线Out-Null喜欢这样:

Notepad.exe | Out-Null

PowerShell将等待,直到退出Notepad.exe进程,然后再继续。这很漂亮,但是从阅读代码中可以看出有些微妙。您还可以将Start-Process与-Wait参数一起使用:

Start-Process <path to exe> -NoNewWindow -Wait

如果您使用的是PowerShell社区扩展版本,则为:

$proc = Start-Process <path to exe> -NoNewWindow -PassThru
$proc.WaitForExit()

PowerShell 2.0中的另一个选项是使用后台作业:

$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job

2
我的错。Start-Process -Wait的工作原理很好,但是现在我明白了,这不是我想要的...我实际上是在等待vm完成引导。我想那将是艰难的。我想我必须为此找到一个新线程。谢谢,但是。
John Mee

7
出色,| out-null正是我所需要的。尝试过使用,Start-Job但是因为我将函数的结果作为参数传递,所以我有点不满意,所以我不能使用最后的建议……
jcolebrand 2012年

6
附带说明一下,如果您需要传递多个参数-ArgumentList,请使用逗号分隔它们,例如-ArgumentList /D=test,/S
sschuberth 2015年

1
感谢您提供简单的“ <exe路径> |空值”解决方案!“启动进程<exe的路径> -NoNewWindow -Wait”方法的问题在于,PowerShell会暂停直到父级派生的所有子级进程完成为止,即使父级在它们之前终止。这导致了我们的安装程序问题。
zax

这就是我用来等待VM启动Start-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName while(((Get-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName -Status |`select -ExpandProperty Statuses |`?{$ _.Code -match“ PowerState”} |`select -ExpandProperty DisplayStatus)-ne“ VM running”){Start-Sleep -s 2} Start-Sleep -s 5 ##给VM时间,以便它可以接受远程请求
andrewmo '18

47

除了使用之外,通过Start-Process -Wait管道传递可执行文件的输出将使Powershell等待。根据不同的需要,我通常会管Out-NullOut-DefaultOut-StringOut-String -Stream是其他一些输出选项的一长串。

# Saving output as a string to a variable.
$output = ping.exe example.com | Out-String

# Filtering the output.
ping stackoverflow.com | where { $_ -match '^reply' }

# Using Start-Process affords the most control.
Start-Process -Wait SomeExecutable.com

我确实想念您引用的CMD / Bash样式运算符(&,&&,||)。看来我们对Powershell更加详细


如果您需要解析输出,那么这是一个非常好的解决方案!
Jan Rothkegel '18

迅速指出Out-xxxx重定向器列表使我理解为什么我应该使用Out-Default而不是Out-Null,因为我需要保留控制台输出。
朱利奥·诺布雷

请注意,同步执行控制台应用程序不需要任何额外的工作-就像在任何shell中一样,这是默认行为。用管道将输出更改单个多行字符串,而PowerShell默认情况下会返回一个lines数组。应避免对控制台应用程序使用(除非您确实希望在新窗口中运行它们),因为您将无法捕获或重定向它们的输出。Out-String Start-Process
mklement0

实际上,本机命令是同步运行(带输出)还是异步运行取决于可执行文件。例如,在不捕获输出的情况下,以下仅输出“宾果”。&'C:\ Program Files \ Mozilla Firefox \ firefox.exe'-?; '宾果'
内森·哈特利

12

只需使用“等待过程”:

"notepad","calc","wmplayer" | ForEach-Object {Start-Process $_} | Wait-Process ;dir

工作完成


8

如果您使用 Start-Process <path to exe> -NoNewWindow -Wait

您也可以使用该-PassThru选项回显输出。


请注意,-PassThru它不会回显输出(按定义,非控制台应用程序不会产生控制台输出),它会输出一个System.Diagnostics.Process代表新启动的进程的实例,因此您可以检查其属性并等待其以后退出。
mklement0

6

某些程序无法很好地处理输出流,使用管道Out-Null可能无法阻止输出流。
而且Start-Process需要-ArgumentList开关来传递参数,不太方便。
还有另一种方法。

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)

1
该调用中的多个参数如何工作?如何定界?一个如何处理各种嵌套字符串转义?ty!
AnneTheAgile 2015年

1
@AnneTheAgile 文档 使用空格分隔参数,以便转义字符使用反斜杠
ifree

ty @ifree,我确实可以通过这种方式运行testcomplete!我在用空格分隔的参数列表周围使用单引号。[1]; 但是现在echo $ exitcode = false,这不是我的返回码吗?[1] $ exitCode = [Diagnostics.Process] :: Start(“ c:\ Program Files(x86)\ SmartBear \ TestComplete 10 \ Bin \ TestComplete.exe”,'“ c:\ Users \ ME \ Documents \ TestComplete 10 Projects \ hig4TestProject1 \ hig4TestProject1.pjs“ / run / project:myProj / test:” KeywordTests | zTdd1_Good“ / exit')。WaitForExit(60)
AnneTheAgile 2015年

3

包括选项 -NoNewWindow给我一个错误:Start-Process : This command cannot be executed due to the error: Access is denied.

我可以使其正常工作的唯一方法是致电:

Start-Process <path to exe> -Wait

0

更进一步,您甚至可以即时解析

例如

& "my.exe" | %{
    if ($_ -match 'OK')
    { Write-Host $_ -f Green }
    else if ($_ -match 'FAIL|ERROR')
    { Write-Host $_ -f Red }
    else 
    { Write-Host $_ }
}

0

总有cmd。如果您在引用启动过程的参数时遇到麻烦,可能会比较烦人:

cmd /c start /wait notepad
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.