Powershell équivalent de bash esperluette (&) pour la fourche / l'exécution de processus d'arrière-plan

158

Dans bash, l'esperluette (&) peut être utilisée pour exécuter une commande en arrière-plan et renvoyer le contrôle interactif à l'utilisateur avant la fin de l'exécution de la commande. Existe-t-il une méthode équivalente pour faire cela dans Powershell?

Exemple d'utilisation dans bash:

 sleep 30 &
Eddiegroves
la source

Réponses:

125

Tant que la commande est un exécutable ou un fichier associé à un exécutable, utilisez Start-Process (disponible à partir de la v2):

Start-Process -NoNewWindow ping google.com

Vous pouvez également l'ajouter en tant que fonction dans votre profil:

function bg() {Start-Process -NoNewWindow @args}

puis l'invocation devient:

bg ping google.com

À mon avis, Start-Job est une exagération pour le cas d'utilisation simple de l'exécution d'un processus en arrière-plan:

  1. Start-Job n'a pas accès à votre étendue existante (car elle s'exécute dans une session distincte). Vous ne pouvez pas faire "Start-Job {notepad $ myfile}"
  2. Start-Job ne conserve pas le répertoire courant (car il s'exécute dans une session distincte). Vous ne pouvez pas faire "Start-Job {notepad myfile.txt}" où myfile.txt est dans le répertoire courant.
  3. La sortie ne s'affiche pas automatiquement. Vous devez exécuter Receive-Job avec l'ID du travail comme paramètre.

REMARQUE: En ce qui concerne votre exemple initial, «bg sleep 30» ne fonctionnera pas car sleep est une commande Powershell. Start-Process ne fonctionne que lorsque vous créez un processus.

Bogdan Calmac
la source
7
Heureux d'avoir trouvé cette réponse. Je cherchais l'équivalent d'un mécanisme Unix fork-double. Pour moi, il semble que quelque chose a commencé avec Start-Jobsera tué lorsque le shell PS sortira. En revanche, il semble que quelque chose qui a commencé avec Start-Processcontinuera à fonctionner après la sortie du shell PS. C'est une différence majeure .
peterh
Oui - si vous démarrez un script en utilisant Start-Process, il survivra à la fin du shell, mais si vous l'avez démarré à partir d'une fenêtre de console, il reste lié à cette fenêtre et la fermeture de la fenêtre mettra fin au processus.
Guss
1
Notez toutefois que vous ne pouvez pas faire la redirection de sortie avec Start-Processet donc cela ne fonctionnera pas: Start-Process {ping -n 1000 example.com > ping__example.com.txt }. La même chose avec Start-Jobfonctionne bien (même si vous devez utiliser le chemin complet vers le fichier de sortie).
Nux
1
Tentative de faire cela pour exécuter un serveur Web de nœud, et le processus se termine lorsque je quitte PowerShell. Quelqu'un sait pourquoi?
Jel
@Jel Non, mais le commentaire de Guss en parle.
jpaugh
26

Il semble que le bloc de script passé à Start-Jobn'est pas exécuté avec le même répertoire courant que la Start-Jobcommande, assurez-vous donc de spécifier un chemin complet si nécessaire.

Par exemple:

Start-Job { C:\absolute\path\to\command.exe --afileparameter C:\absolute\path\to\file.txt }
Gian Marco
la source
3
Ainsi, par exemple, si je dois exécuter Git Pull dans la ligne de commande, je dois spécifier le chemin complet de tout ...? C'est vraiment ennuyeux.
CMCDragonkai
1
merci, bon conseil, je faisais le tour d'un travail qui ne fonctionnait pas, c'était la raison pour laquelle.
Omni
@CMCDragonkai ou vous pouvez simplement faire Start-Job {cd C: \ Path \ To \ Repo; git pull}
Mahomedalid
21
ps2> start-job {start-sleep 20}

je n'ai pas encore compris comment obtenir stdout en temps réel, start-job vous oblige à interroger stdout avec get-job

mise à jour: je ne pouvais pas démarrer le travail pour faire facilement ce que je veux, qui est essentiellement l'opérateur bash &. voici mon meilleur hack jusqu'à présent

PS> notepad $profile #edit init script -- added these lines
function beep { write-host `a }
function ajp { start powershell {ant java-platform|out-null;beep} } #new window, stderr only, beep when done
function acjp { start powershell {ant clean java-platform|out-null;beep} }
PS> . $profile #re-load profile script
PS> ajp
Dustin Getz
la source
1
À partir de PowerShell v3.0, vous pouvez obtenir stdout en temps réel comme ceci:Start-Job { Write-Output 'Hello world' } | Receive-Job -Wait
JamesQMurphy
19

À partir de PowerShell Core 6.0, vous pouvez écrire &à la fin de la commande et cela équivaudra à exécuter votre pipeline en arrière-plan dans le répertoire de travail actuel .

Ce n'est pas équivalent à &dans bash, c'est juste une syntaxe plus agréable pour la fonctionnalité de tâches PowerShell actuelle . Il renvoie un objet de travail afin que vous puissiez utiliser toutes les autres commandes que vous utiliseriez pour les travaux. Par exemple Receive-Job:

C:\utils> ping google.com &

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
35     Job35           BackgroundJob   Running       True            localhost            Microsoft.PowerShell.M...


C:\utils> Receive-Job 35

Pinging google.com [172.217.16.14] with 32 bytes of data:
Reply from 172.217.16.14: bytes=32 time=11ms TTL=55
Reply from 172.217.16.14: bytes=32 time=11ms TTL=55
Reply from 172.217.16.14: bytes=32 time=10ms TTL=55
Reply from 172.217.16.14: bytes=32 time=10ms TTL=55

Ping statistics for 172.217.16.14:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 10ms, Maximum = 11ms, Average = 10ms
C:\utils>

Si vous souhaitez exécuter quelques instructions en arrière-plan, vous pouvez combiner l' &opérateur d'appel , le { }bloc de script et ce nouvel &opérateur d'arrière-plan comme ici:

& { cd .\SomeDir\; .\SomeLongRunningOperation.bat; cd ..; } &

Voici quelques informations supplémentaires tirées des pages de documentation:

à partir de Quoi de neuf dans PowerShell Core 6.0 :

Prise en charge de l'arrière-plan des pipelines avec esperluette (&) (# 3360)

Le placement &à la fin d'un pipeline entraîne l'exécution du pipeline en tant que travail PowerShell. Lorsqu'un pipeline est en arrière-plan, un objet de travail est renvoyé. Une fois que le pipeline s'exécute en tant que travail, toutes les *-Jobapplets de commande standard peuvent être utilisées pour gérer le travail. Les variables (ignorant les variables spécifiques au processus) utilisées dans le pipeline sont automatiquement copiées dans le travail et Copy-Item $foo $bar &fonctionnent donc. Le travail est également exécuté dans le répertoire courant au lieu du répertoire de base de l'utilisateur. Pour plus d'informations sur les travaux PowerShell, consultez about_Jobs .

à partir de l' opérateur about_operators / Ampersand background & :

Opérateur d'arrière-plan esperluette &

Exécute le pipeline avant celui-ci dans une tâche PowerShell. L'opérateur d'arrière-plan et d'esperluette agit de la même manière que «l'opérateur d'esperluette» UNIX qui exécute la commande avant lui en tant que processus d'arrière-plan. L'opérateur d'arrière-plan esperluette est construit au-dessus des travaux PowerShell, de sorte qu'il partage beaucoup de fonctionnalités avec Start-Job. La commande suivante contient l'utilisation de base de l'opérateur d'arrière-plan esperluette.

Get-Process -Name pwsh &

Ceci est fonctionnellement équivalent à l'utilisation suivante de Start-Job.

Start-Job -ScriptBlock {Get-Process -Name pwsh}

Comme il est fonctionnellement équivalent à utiliser Start-Job, l'opérateur d'arrière-plan esperluette renvoie un Jobobjet exactement comme Start-Job does. Cela signifie que vous pouvez utiliser Receive-Jobet Remove-Jobtout comme vous le feriez si vous aviez l'habitude Start-Jobde démarrer le travail.

$job = Get-Process -Name pwsh &
Receive-Job $job

Production

NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
------    -----      -----     ------      --  -- -----------
    0     0.00     221.16      25.90    6988 988 pwsh
    0     0.00     140.12      29.87   14845 845 pwsh
    0     0.00      85.51       0.91   19639 988 pwsh


$job = Get-Process -Name pwsh &
Remove-Job $job

Pour plus d'informations sur les travaux PowerShell, consultez about_Jobs .

Mariusz Pawelski
la source
1
Merci pour ce bon résumé que des centaines de pages MS n'ont pas pu accomplir.
not2qubit
Une idée comment gérer les tâches de la console ? J'ai essayé ce qui précède avec Receive-Job 3, mais rien ne se passe.
not2qubit
@ not2qubit Je ne suis pas sûr de ce que vous entendez par « travaux de console ». Quelle commande exécutez-vous que vous souhaitez exécuter en arrière-plan?
Mariusz Pawelski
J'utilisais une application qui lance une console Windows, mais je ne peux pas obtenir de sortie et j'espérais l'obtenir de la même manière que le monde nix fgpour mettre le travail au premier plan * .
not2qubit
@ not2qubit Malheureusement, il semble qu'il n'y ait rien de semblable à unix fgdans PowerShell: (Je me souviens l'avoir cherché, mais je
n'ai
17

Vous pouvez utiliser les applets de commande PowerShell pour atteindre vos objectifs.

Il existe 6 applets de commande liées au travail disponibles dans PowerShell.

  • Get-Job
    • Obtient les travaux d'arrière-plan Windows PowerShell qui s'exécutent dans la session en cours
  • Recevoir-Job
    • Obtient les résultats des travaux d'arrière-plan Windows PowerShell dans la session en cours
  • Remove-Job
    • Supprime une tâche d'arrière-plan Windows PowerShell
  • Start-Job
    • Démarre une tâche d'arrière-plan Windows PowerShell
  • Stop-Job
    • Arrête une tâche d'arrière-plan Windows PowerShell
  • Wait-Job
    • Supprime l'invite de commande jusqu'à ce qu'une ou toutes les tâches d'arrière-plan Windows PowerShell exécutées dans la session soient terminées

Si cela vous intéresse, vous pouvez télécharger l'exemple Comment créer un travail d'arrière-plan dans PowerShell

Eric
la source
1
Bonne réponse car il traite de Powershell 3+. Quelle est la différence entre les applets de commande de travail et les réponses précédentes?
Jeremy Hajek
3

Vous pouvez faire quelque chose comme ça.

$a = start-process -NoNewWindow powershell {timeout 10; 'done'} -PassThru

Et si vous voulez l'attendre:

$a | wait-process

Bonus version OSX ou Linux:

$a = start-process pwsh '-c',{start-sleep 5; 'done'} -PassThru 

Exemple de script de pinger que j'ai. Les arguments sont passés sous forme de tableau:

$1 = start -n powershell pinger,comp001 -pa
js2010
la source
start va bifurquer le processus et retourner le contrôle à l'appelant. Plus d'infos ici
Aethalides
@Aethalides start est en fait un alias pour start-process dans PowerShell.
js2010
2

tl; dr

Start-Process powershell { sleep 30 }
bence de l'espace extra-atmosphérique
la source