Bash prend-il en charge le forking similaire à fork () de C?

25

J'ai un script que je voudrais déboucher à un moment donné, donc deux copies du même script sont en cours d'exécution.

Par exemple, j'aimerais que le script bash suivant existe:

echo $$
do_fork()
echo $$

Si ce script bash existait vraiment, la sortie attendue serait:

<ProcessA PID>
<ProcessB PID>
<ProcessA PID>

ou

<ProcessA PID>
<ProcessA PID>
<ProcessB PID>

Y a-t-il quelque chose que je peux mettre à la place de "do_fork ()" pour obtenir ce type de sortie, ou pour que le script bash fasse un fork de type C?

Cory Klein
la source

Réponses:

23

Oui. Forking s'écrit &:

echo child & echo parent

Ce qui peut vous dérouter, c'est que ce $$n'est pas le PID du processus shell, c'est le PID du processus shell d' origine . Le fait de le faire de cette façon est qu'il $$s'agit d'un identifiant unique pour une instance particulière du script shell: il ne change pas pendant l'exécution du script, et il est différent de $$tout autre script exécuté simultanément. Une façon d'obtenir le PID réel du processus shell est sh -c 'echo $PPID'.

Le flux de contrôle dans le shell n'est pas le même que C. Si en C, vous écririez

first(); fork(); second(); third();

alors un équivalent shell est

after_fork () { second; third; }
first; after_fork & after_fork

La forme simple du shell first; child & parentcorrespond à l'idiome C habituel

first(); if (fork()) parent(); else child();

&et $$existent et se comportent de cette façon dans chaque shell de style Bourne et dans (t) csh. $PPIDn'existait pas dans le shell d'origine Bourne mais est dans POSIX (donc c'est en ash, bash, ksh, zsh,…).

Gilles 'SO- arrête d'être méchant'
la source
3
Mais c'est fondamentalement "fork + exec", pas seulement fork.
mattdm
@mattdm: Euh? &est fork, il n'y a pas d'exec impliqué. Fork + exec est lorsque vous lancez une commande externe.
Gilles 'SO- arrête d'être méchant'
@mattdm: Ah, je pense que je vois où Cory veut en venir. Il n'y a pas exec, mais les deux langues ont un flux de contrôle différent.
Gilles 'SO- arrête d'être méchant'
1
@Gilles: L'opérateur de contrôle bash &démarre un sous-shell dans lequel la commande donnée est exécutée. Fork + exec. Vous ne pouvez pas simplement &exécuter sans commande précédente à exécuter.
mattdm
5
Eh bien, si "Forking est orthographié &" était une vraie déclaration, vous pourriez mettre &une ligne par lui-même. Mais tu ne peux pas. Cela ne signifie pas "bifurquer ici". Cela signifie "exécuter la commande précédente en arrière-plan dans un sous-shell".
mattdm
10

Oui, ça s'appelle des sous-coquilles . Le code shell entre parenthèses est exécuté comme un sous-shell (fork). Cependant, le premier shell attend normalement la fin de l'enfant. Vous pouvez le rendre asynchrone à l'aide du &terminateur. Voyez-le en action avec quelque chose comme ceci:

#!/bin/bash

(sleep 2; echo "subsh 1")&
echo "topsh"

$ bash subsh.sh

Keith
la source
5
Les parenthèses créent un sous-shell, mais c'est fork + wait. &est une fourchette seule. Si vous voulez exécuter plus d'un pipeline dans le processus enfant, il suffit d'utiliser des accolades (qui effectuent le regroupement sans créer de processus enfant):{ sleep 2; echo child; } &
Gilles 'SO- arrête d'être méchant'
6

Il n'y a pas de méthode bash native (ou, à ma connaissance, tout autre shell * nix typique) pour le faire. Il existe de nombreuses façons de générer des processus forkés qui font autre chose de manière asynchrone, mais je ne pense pas qu'il y ait quoi que ce soit qui suive la sémantique exacte de l'appel système fork ().

L'approche typique consiste à faire en sorte que votre script de niveau supérieur génère des assistants qui effectuent exactement le travail que vous souhaitez répartir. Si vous le faites $0 $@ &ou quoi que ce soit, vous recommencerez au début et devrez le comprendre d'une manière ou d'une autre.

En fait, je commence à penser à plusieurs façons intelligentes dont on pourrait faire exactement cela ...

Mais , avant que mon cerveau ne s'emballe trop, je pense qu'une assez bonne règle est la suivante: si vous essayez d'écrire quelque chose dans le shell et qu'il y a plein d'astuces intelligentes et que vous souhaitez plus de fonctionnalités linguistiques, il est temps de changer à un vrai langage de programmation .

mattdm
la source
2
Bien que votre point soit bien pris en compte - la syntaxe de bash est sans doute difficile et il manque les structures de données et les fonctionnalités les plus élégantes de Perl ou Python, bash est aussi réel que possible - dans son domaine . C'est un langage spécifique au domaine, et je dirais même mieux (plus succinct, plus simple) que par exemple Python dans de nombreux cas. Pouvez-vous administrer une multitude de systèmes et ne pas connaître Python? Oui. Pouvez-vous faire cela et ne pas connaître un coup de bash [programmation]? Je ne voudrais pas essayer. Après 30 ans de programmation shell, je vous dis que c'est aussi réel que possible. Et oui, je parle Python. Mais ne confondez pas les jeunes.
Mike S
2
Cela dit, techniquement, j'aime plus cette réponse. Dire que «&» est la réponse à la question de l'utilisateur, «bifurquer à un moment donné pour que deux copies du même script soient en cours d'exécution» est à mon avis déroutant. Selon le manuel de bash, ce que "&" fait est "... exécute la commande [given] en arrière-plan dans un sous-shell." Il doit terminer une commande, et il implique un fork (techniquement, sous Linux, un clone ()), mais à ce stade, deux copies du même script ne sont pas en cours d'exécution.
Mike S