Comment dire à PowerShell d'attendre la fin de chaque commande avant de commencer la suivante?

212

J'ai un script PowerShell 1.0 pour simplement ouvrir un tas d'applications. Le premier est une machine virtuelle et les autres sont des applications de développement. Je veux que la machine virtuelle termine le démarrage avant que les autres applications ne soient ouvertes.

En bash je pourrais juste dire "cmd1 && cmd2"

Voilà ce que j'ai ...

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

Réponses:

367

Normalement, pour les commandes internes, PowerShell attend avant de démarrer la commande suivante. Une exception à cette règle est l'EXE basé sur le sous-système Windows externe. La première astuce consiste à canaliser pour Out-Nullaimer:

Notepad.exe | Out-Null

PowerShell attendra la fin du processus Notepad.exe avant de continuer. C'est astucieux mais assez subtil à reprendre à la lecture du code. Vous pouvez également utiliser Start-Process avec le paramètre -Wait:

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

Si vous utilisez la version des extensions de communauté PowerShell, c'est:

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

Une autre option dans PowerShell 2.0 consiste à utiliser un travail d'arrière-plan:

$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Keith Hill
la source
2
Ma faute. Start-Process -Wait fonctionne très bien, mais maintenant je vois que ce n'est pas ce que je cherchais ... Je cherche en fait à attendre que le vm termine de démarrer. J'imagine que ça va être difficile. Je suppose que je vais devoir trouver un nouveau fil pour ça. Merci mais.
John Mee
7
Brillant, a | out-nullfait exactement ce dont j'avais besoin. Start-JobJ'ai essayé d'utiliser, mais parce que je passe les résultats des fonctions en tant que paramètres, cela m'a un peu déçu, donc je n'ai pas pu utiliser la dernière suggestion ...
jcolebrand
6
En remarque, si vous devez passer plusieurs arguments avec -ArgumentList, séparez-les avec des virgules comme -ArgumentList /D=test,/S.
sschuberth
1
Merci pour la solution simple "<chemin vers l'exe> | Out-Null"! Le problème avec la méthode "Start-Process <path to exe> -NoNewWindow -Wait" est que PowerShell fait une pause jusqu'à ce que tous les processus enfants générés par le parent soient terminés, même si le parent se termine avant eux. Cela a provoqué nos problèmes de programme d'installation.
zax
Voici ce que j'utilise pour attendre qu'une machine virtuelle démarre 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 ## Donnez le temps à la VM de monter pour qu'elle puisse accepter demandes à distance
andrewmo
47

Outre l'utilisation Start-Process -Wait, le fait de canaliser la sortie d'un exécutable fera attendre Powershell. En fonction des besoins, je vais généralement conduit à Out-Null, Out-Default, Out-Stringou Out-String -Stream. Voici une longue liste d'autres options de sortie.

# 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

Je manque les opérateurs de style CMD / Bash que vous avez référencés (&, &&, ||). Il semble que nous devons être plus verbeux avec Powershell .

Nathan Hartley
la source
C'est une très bonne solution si vous avez besoin d'analyser la sortie!
Jan Rothkegel
Le fait de pointer la liste des redirecteurs Out-xxxx m'a rapidement permis de comprendre pourquoi je devrais utiliser Out-Default au lieu d' Out-Null , car j'avais besoin de conserver la sortie de la console.
Julio Nobre
Notez qu'aucun travail supplémentaire n'est nécessaire pour exécuter les applications de console de manière synchrone - comme dans n'importe quel shell, c'est le comportement par défaut. Le piping Out-String change la sortie en une seule chaîne de plusieurs lignes , tandis que PowerShell renvoie par défaut un tableau de lignes . Start-Processdevrait être évité pour les applications de console (sauf si vous voulez vraiment les exécuter dans une nouvelle fenêtre ) car vous ne pourrez pas capturer ou rediriger leur sortie.
mklement0
En fait, le fait qu'une commande native s'exécute de manière synchrone (avec sortie) ou asynchrone dépend de l'exécutable. Par exemple, sans capturer la sortie, ce qui suit ne produit que "bingo". & 'C: \ Program Files \ Mozilla Firefox \ firefox.exe' -? ; 'bingo'
Nathan Hartley
12

Utilisez simplement "Wait-process":

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

la tâche est finie

Flaviofire
la source
8

Si tu utilises Start-Process <path to exe> -NoNewWindow -Wait

Vous pouvez également utiliser l' -PassThruoption pour faire écho à la sortie.

Sandy Chapman
la source
Notez que -PassThrune fait pas écho à la sortie (une application non console par définition ne produira pas de sortie console), il génère une System.Diagnostics.Processinstance qui représente le processus nouvellement lancé, vous pouvez donc examiner ses propriétés et attendre qu'il se termine plus tard.
mklement0
6

Certains programmes ne peuvent pas très bien traiter le flux de sortie, en utilisant pipe pour Out-Nullne pas le bloquer.
Et a Start-Processbesoin du -ArgumentListcommutateur pour passer des arguments, pas si pratique.
Il existe également une autre approche.

$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
je libère
la source
1
comment fonctionnent plusieurs arguments dans cet appel? comment sont-ils délimités? comment gère-t-on les différents échappements de chaînes imbriquées? ty!
AnneTheAgile
1
@AnneTheAgile doc utilise un espace pour séparer les arguments, pour les caractères qui s'échappent, utilisez une barre oblique inverse
ifree
ty @ifree, j'ai réussi à terminer le test de cette façon! J'ai utilisé des guillemets simples autour de la liste des arguments séparés par des espaces. [1]; mais maintenant echo $ exitcode = false, quel n'était pas mon code de retour du processus? [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
3

L'inclusion de l'option -NoNewWindowme donne une erreur:Start-Process : This command cannot be executed due to the error: Access is denied.

La seule façon de le faire fonctionner était d'appeler:

Start-Process <path to exe> -Wait
twasbrillig
la source
0

En allant plus loin, vous pouvez même analyser à la volée

par exemple

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

Il y a toujours cmd. Cela peut être moins gênant si vous avez du mal à citer des arguments pour démarrer le processus:

cmd /c start /wait notepad
js2010
la source