¿Cómo decirle a PowerShell que espere a que termine cada comando antes de comenzar el siguiente?

212

Tengo un script PowerShell 1.0 para abrir un montón de aplicaciones. La primera es una máquina virtual y las otras son aplicaciones de desarrollo. Quiero que la máquina virtual termine de iniciarse antes de que se abra el resto de las aplicaciones.

En bash solo podría decir "cmd1 && cmd2"

Esto es lo que tengo ...

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

Respuestas:

367

Normalmente, para los comandos internos, PowerShell espera antes de iniciar el siguiente comando. Una excepción a esta regla es el EXE externo basado en el subsistema de Windows. El primer truco es canalizar para Out-Nullgustar así:

Notepad.exe | Out-Null

PowerShell esperará hasta que el proceso Notepad.exe haya salido antes de continuar. Eso es ingenioso pero algo sutil de leer al leer el código. También puede usar Start-Process con el parámetro -Wait:

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

Si está utilizando la versión de PowerShell Community Extensions es:

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

Otra opción en PowerShell 2.0 es usar un trabajo en segundo plano:

$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Keith Hill
fuente
2
Culpa mía. Start-Process -Wait funciona muy bien, pero ahora veo que esto no es lo que estaba buscando ... En realidad estoy tratando de esperar hasta que el vm termine de arrancar. Me imagino que va a ser difícil. Supongo que tendré que encontrar un nuevo hilo para eso. Gracias pero.
John Mee el
77
Brillante, | out-nullhice justo lo que necesitaba. Trató de usar Start-Job, sino porque estoy pasando los resultados de las funciones como parámetros, se puso un poco skitzoid en mí, así que no podía usar la última sugerencia ...
jcolebrand
66
Como nota al margen, si necesita pasar varios argumentos con -ArgumentList, sepárelos con comas como -ArgumentList /D=test,/S.
sschuberth
1
¡Gracias por la solución simple "<ruta de acceso a exe> | Out-Null"! El problema con el método "Start-Process <path to exe> -NoNewWindow -Wait" es que PowerShell hace una pausa hasta que todos los procesos secundarios generados por el padre estén completos, incluso si el padre termina antes de ellos. Esto causó problemas con nuestro programa de instalación.
zax
Esto es lo que uso para esperar a que una VM inicie 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 ## Dé tiempo a la VM para que aparezca y así pueda aceptar solicitudes remotas
andrewmo
47

Además de usar Start-Process -Wait, canalizar la salida de un ejecutable hará que Powershell espere. Dependiendo de la necesidad, lo haré normalmente tubería a Out-Null, Out-Default, Out-Stringo Out-String -Stream. Aquí hay una larga lista de algunas otras opciones de salida.

# 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

Echo de menos los operadores de estilo CMD / Bash a los que hizo referencia (&, &&, ||). Parece que tenemos que ser más detallados con Powershell .

Nathan Hartley
fuente
¡Esta es una solución increíblemente buena si necesita analizar la salida!
Jan Rothkegel
Señalar la lista de redireccionadores Out-xxxx rápidamente me permitió comprender por qué debería usar Out-Default en lugar de Out-Null , ya que necesitaba mantener la salida de la consola.
Julio Nobre
Tenga en cuenta que no se necesita trabajo adicional para ejecutar aplicaciones de consola de forma síncrona, como en cualquier shell, ese es el comportamiento predeterminado. La canalización a Out-String cambia la salida a una sola cadena de varias líneas , mientras que PowerShell por defecto devuelve una matriz de líneas . Start-Processdebe evitarse para aplicaciones de consola (a menos que realmente desee ejecutarlas en una nueva ventana ) porque no podrá capturar o redirigir su salida.
mklement0
En realidad, si un comando nativo se ejecuta sincrónicamente (con salida) o asíncronamente depende del ejecutable. Como ejemplo, sin capturar la salida, lo siguiente solo genera "bingo". & 'C: \ Archivos de programa \ Mozilla Firefox \ firefox.exe' -? ; 'bingo'
Nathan Hartley
12

Simplemente use "Proceso de espera":

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

el trabajo esta terminado

Flaviofire
fuente
8

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

También puede usar la -PassThruopción de eco de salida.

Sandy Chapman
fuente
Tenga en cuenta que -PassThruno emite salida (una aplicación que no es de consola, por definición, no producirá salida de consola), emite una System.Diagnostics.Processinstancia que representa el proceso recién lanzado, por lo que puede examinar sus propiedades y esperar a que salga más tarde.
mklement0
6

Algunos programas no pueden procesar el flujo de salida muy bien, por lo que el uso de tubería Out-Nullpuede no bloquearlo.
Y Start-Processnecesita el -ArgumentListcambio para pasar argumentos, no tan conveniente.
También hay otro enfoque.

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
ifree
fuente
1
¿Cómo funcionan los argumentos múltiples en esa llamada? ¿Cómo se delimitan? ¿Cómo se maneja el escape de varias cadenas anidadas? ty!
AnneTheAgile
1
@AnneTheAgile doc usa espacio para separar los argumentos, para escapar de caracteres usa barra invertida
ifree
ty @ifree, ¡obtuve testcomplete para ejecutar de esa manera! Usé comillas simples alrededor de la lista de argumentos delimitados por espacios. [1]; pero ahora echo $ exitcode = false, ¿cuál no fue mi código de retorno del proceso? [1] $ exitCode = [Diagnostics.Process] :: Start ("c: \ Archivos de programa (x86) \ SmartBear \ TestComplete 10 \ Bin \ TestComplete.exe", '"c: \ Users \ ME \ Documents \ TestComplete 10 Proyectos \ hig4TestProject1 \ hig4TestProject1.pjs "/ run / project: myProj / test:" KeywordTests | zTdd1_Good "/ exit ') .WaitForExit (60)
AnneTheAgile
3

Incluir la opción -NoNewWindowme da un error:Start-Process : This command cannot be executed due to the error: Access is denied.

La única forma en que pude hacerlo funcionar fue llamando:

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

Llevándolo más lejos, incluso podrías analizarlo sobre la marcha

p.ej

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

Siempre hay cmd. Puede ser menos molesto si tiene problemas para citar argumentos para iniciar el proceso:

cmd /c start /wait notepad
js2010
fuente