Como dizer ao PowerShell para esperar que cada comando termine antes de iniciar o próximo?

211

Eu tenho um script do PowerShell 1.0 para abrir apenas um monte de aplicativos. O primeiro é uma máquina virtual e os outros são aplicativos de desenvolvimento. Quero que a máquina virtual conclua a inicialização antes que o restante dos aplicativos seja aberto.

Na festança, eu poderia dizer "cmd1 && cmd2"

É isso que eu tenho ...

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

Respostas:

366

Normalmente, para comandos internos, o PowerShell espera antes de iniciar o próximo comando. Uma exceção a essa regra é o EXE externo baseado no subsistema Windows. O primeiro truque é fazer o pipeline para Out-Null:

Notepad.exe | Out-Null

O PowerShell aguardará até que o processo do Notepad.exe seja encerrado antes de continuar. Isso é bacana, mas meio sutil de aprender com a leitura do código. Você também pode usar o Start-Process com o parâmetro -Wait:

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

Se você estiver usando a versão das Extensões da Comunidade do PowerShell, é:

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

Outra opção no PowerShell 2.0 é usar um trabalho em segundo plano:

$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Keith Hill
fonte
2
Foi mal. Start-Process -Wait funciona muito bem, mas agora vejo que não é isso que eu estava procurando ... Na verdade, estou procurando esperar até que a VM termine a inicialização. Eu imagino que vai ser difícil. Suponho que terei que encontrar um novo tópico para isso. Obrigado, mas.
John Mee
7
Brilhante, | out-nullfiz exatamente o que eu precisava. Tentei usar Start-Job, mas porque eu estou passando os resultados de funções como parâmetros, ele ficou um pouco skitzoid em mim, então eu não poderia usar a última sugestão ...
jcolebrand
6
Como observação lateral, se você precisar passar vários argumentos com -ArgumentList, separe-os com vírgulas como -ArgumentList /D=test,/S.
Sschuberth 04/09/2015
1
Obrigado pela solução simples "<path to exe> | Out-Null"! O problema com o método "o processo Start-process <caminho para o exe> -NoNewWindow -Wait" é que o PowerShell faz uma pausa até que todos os processos filhos gerados pelo pai sejam concluídos, mesmo se o pai terminar antes deles. Isso causou problemas no nosso programa de instalação.
Zax3 /
É isso que eu uso para aguardar uma VM iniciar o Start-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName while ((Get-AzureRmVM -ResourceGroupName $ ResourceGroupName -Name $ VmName -Status | `select -ExpandProperty Status |`? _.Code -match "PowerState"} | `select -ExpandProperty DisplayStatus) -ne" VM running ") {Start-Sleep -s 2} Start-Sleep -s 2} Start-Sleep -s 5 ## Dê tempo à VM para que ela aconteça e aceite pedidos remotos
andrewmo
47

Além de usar Start-Process -Wait, canalizar a saída de um executável fará o Powershell esperar. Dependendo da necessidade, eu vou tipicamente tubo para Out-Null, Out-Default, Out-Stringou Out-String -Stream. Aqui está uma longa lista de outras opções de saída.

# 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

Sinto falta dos operadores de estilo CMD / Bash que você referenciou (&, &&, ||). Parece que temos que ser mais detalhados com o Powershell .

Nathan Hartley
fonte
Esta é uma solução incrivelmente boa se você precisar analisar a saída!
Jan Rothkegel
Apontar a lista de redirecionadores Out-xxxx rapidamente me permitiu entender por que eu deveria usar Out-Default em vez de Out-Null , pois precisava manter a saída do console.
Julio Nobre
Observe que nenhum trabalho extra é necessário para executar aplicativos de console de forma síncrona - como em qualquer shell, esse é o comportamento padrão. A canalização para Out-String altera a saída para uma única sequência de várias linhas , enquanto o PowerShell, por padrão, retorna uma matriz de linhas . Start-Processdeve ser evitado para aplicativos de console (a menos que você realmente queira executá-los em uma nova janela ) porque você não poderá capturar ou redirecionar a saída deles.
mklement0
Na verdade, se um comando nativo é executado de forma síncrona (com saída) ou assíncrona depende do executável. Por exemplo, sem capturar a saída, o seguinte gera apenas "bingo". & 'C: \ Arquivos de Programas \ Mozilla Firefox \ firefox.exe' -? ; 'bingo'
Nathan Hartley
12

Basta usar "Wait-process":

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

trabalho está feito

Flaviofire
fonte
8

Se você usar Start-Process <path to exe> -NoNewWindow -Wait

Você também pode usar a -PassThruopção para ecoar a saída.

Sandy Chapman
fonte
Observe que -PassThrunão ecoa a saída (um aplicativo que não é do console, por definição, não produz saída do console); ele gera uma System.Diagnostics.Processinstância que representa o processo recém-iniciado, portanto você pode examinar suas propriedades e aguardar a saída mais tarde.
mklement0
6

Alguns programas não conseguem processar muito bem o fluxo de saída, usar o pipe para Out-Nullnão bloqueá-lo.
E Start-Processprecisa da -ArgumentListopção de passar argumentos, não tão conveniente.
Há também outra abordagem.

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
eu livre
fonte
1
como funcionam vários argumentos nessa chamada? como eles são delimitados? como lidar com várias seqüências de caracteres aninhadas escapando? ty!
AnneTheAgile
1
@AnneTheAgile doc espaço uso para separar os argumentos, por caractere escapar barra invertida uso
iFree
ty @ free, eu consegui o testcomplete para executar dessa maneira! Eu usei aspas simples em torno da lista de argumentos delimitados por espaço. [1]; mas agora echo $ exitcode = false, qual não era o meu código de retorno do processo? [1] $ exitCode = [Diagnostics.Process] :: Start ("c: \ Arquivos de programas (x86) \ SmartBear \ TestComplete 10 \ Bin \ TestComplete.exe", "" c: \ Usuários \ ME \ Documents \ TestComplete 10 Projetos \ hig4TestProject1 \ hig4TestProject1.pjs "/ execução / projeto: myProj / teste:" KeywordTests | zTdd1_Good "/ exit ') .WaitForExit (60)
AnneTheAgile
3

A inclusão da opção -NoNewWindowgera um erro:Start-Process : This command cannot be executed due to the error: Access is denied.

A única maneira de fazê-lo funcionar era ligar para:

Start-Process <path to exe> -Wait
twasbrillig
fonte
0

Indo mais longe, você pode até analisar rapidamente

por exemplo

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

Sempre há cmd. Pode ser menos irritante se você tiver problemas para citar argumentos para iniciar o processo:

cmd /c start /wait notepad
js2010
fonte