Как заставить PowerShell ждать завершения каждой команды, прежде чем начинать следующую?

212

У меня есть сценарий PowerShell 1.0, чтобы просто открыть кучу приложений. Первая - это виртуальная машина, а остальные - приложения для разработки. Я хочу завершить загрузку виртуальной машины до того, как откроются остальные приложения.

В баш я могу просто сказать "cmd1 && cmd2"

Это то, что у меня есть ...

C:\Applications\VirtualBox\vboxmanage startvm superdooper
    &"C:\Applications\NetBeans 6.5\bin\netbeans.exe"
Джон Ми
источник

Ответы:

367

Обычно для внутренних команд PowerShell ожидает, прежде чем запустить следующую команду. Единственным исключением из этого правила является EXE-файл на основе внешней подсистемы Windows. Первый трюк заключается в том, чтобы конвейер Out-Nullпонравился так:

Notepad.exe | Out-Null

PowerShell будет ожидать завершения процесса Notepad.exe, прежде чем продолжить. Это изящно, но довольно тонко, чтобы разобраться с чтением кода. Вы также можете использовать Start-Process с параметром -Wait:

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

Если вы используете версию PowerShell Community Extensions, это:

$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 прекрасно работает, но теперь я вижу, что это не то, что я искал ... Я на самом деле пытаюсь дождаться окончания загрузки виртуальной машины. Я думаю, что это будет сложно. Я полагаю, мне придется найти новую тему для этого. Спасибо но.
Джон Ми
7
Бриллиант, | out-nullсделал только то, что мне было нужно. Пробовал использовать, Start-Jobно так как я передаю результаты функций в качестве параметров, у меня появился небольшой скитоид, поэтому я не мог использовать последнее предложение ...
jcolebrand
6
В качестве примечания: если вам нужно передать несколько аргументов -ArgumentList, разделите их запятыми, как -ArgumentList /D=test,/S.
Щуберт
1
Спасибо за простое решение <path to exe> | Out-Null! Проблема с методом «Start-Process <путь к exe> -NoNewWindow -Wait» заключается в том, что PowerShell делает паузу до тех пор, пока все дочерние процессы, порожденные родителем, не будут завершены, даже если родительский процесс завершается раньше них. Это вызвало проблемы с нашей программой установки.
Zax
Это то, что я использую для ожидания запуска виртуальной машины Start-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName while ((Get-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName -Status | `select -ExpandProperty Statuses |` _.Code -match "PowerState"} | `select -ExpandProperty DisplayStatus) -ne" Виртуальная машина работает ") {Start-Sleep -s 2} Start-Sleep -s 5 ## Дайте виртуальной машине время на запуск, чтобы она могла принять удаленные запросы
andrewmo
47

Помимо использования Start-Process -Wait, передача вывода исполняемого файла заставит Powershell ждать. В зависимости от потребности, я буду обычно труба , Out-Null, Out-Default, Out-Stringили Out-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 .

Натан Хартли
источник
Это невероятно хорошее решение, если вам нужно разобрать вывод!
Ян Роткегель
Указание списка перенаправителей Out-xxxx быстро позволило мне понять, почему я должен использовать Out-Default вместо Out-Null , так как мне нужно было сохранять вывод консоли.
Хулио Нобре
Обратите внимание, что для синхронного выполнения консольных приложений не требуется никакой дополнительной работы - как в любой оболочке, это поведение по умолчанию. Конвейер изменяет вывод на одну многострочную строку, тогда как PowerShell по умолчанию возвращает массив строк . следует избегать для консольных приложений (если вы действительно не хотите запускать их в новом окне ), потому что вы не сможете захватывать или перенаправлять их вывод. Out-String Start-Process
mklement0
На самом деле, выполняется ли собственная команда синхронно (с выводом) или асинхронно, зависит от исполняемого файла. В качестве примера, без захвата вывода, следующие только выводит «бинго». & 'C: \ Program Files \ Mozilla Firefox \ firefox.exe' -? ; «Бинго»
Натан Хартли
12

Просто используйте «Wait-process»:

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

работа сделана

Flaviofire
источник
8

Если вы используете Start-Process <path to exe> -NoNewWindow -Wait

Вы также можете использовать -PassThruопцию для вывода на экран.

Сэнди Чепмен
источник
Обратите внимание, что -PassThruон не отображает вывод (не консольное приложение по определению не выдаст консольный вывод), он выводит System.Diagnostics.Processэкземпляр, который представляет только что запущенный процесс, поэтому вы можете проверить его свойства и дождаться его выхода позже.
mklement0
6

Некоторые программы не могут обработать поток вывода очень хорошо, используя pipe, чтобы Out-Nullне блокировать его.
И Start-Processнужен -ArgumentListпереключатель для передачи аргументов, не так удобно.
Есть и другой подход.

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
я свободен
источник
1
как работают несколько аргументов в этом вызове? как они разграничены? как обрабатывать различные экранированные вложенные строки? ти!
AnneTheAgile
1
@AnneTheAgile doc использует пробел для разделения аргументов, для экранирования символов используйте обратную косую черту
ifree
ты @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 Проекты \ hig4TestProject1 \ hig4TestProject1.pjs "/ run / project: myProj / test:" KeywordTests | zTdd1_Good "/ exit ') .WaitForExit (60)
AnneTheAgile
3

Включение опции -NoNewWindowдает мне ошибку:Start-Process : This command cannot be executed due to the error: Access is denied.

Единственный способ заставить его работать - позвонить:

Start-Process <path to exe> -Wait
twasbrillig
источник
0

Принимая это дальше, вы можете даже разобрать на лету

например

& "my.exe" | %{
    if ($_ -match 'OK')
    { Write-Host $_ -f Green }
    else if ($_ -match 'FAIL|ERROR')
    { Write-Host $_ -f Red }
    else 
    { Write-Host $_ }
}
Джастин
источник
0

Там всегда CMD. Это может быть менее раздражающим, если у вас есть проблемы с цитированием аргументов для start-process:

cmd /c start /wait notepad
js2010
источник