Substitution de processus et pipe

Je me demandais comment comprendre ce qui suit:

Transférer le stdout dune commande dans le stdin dune autre est une technique puissante. Mais que se passe-t-il si vous devez diriger la sortie standard de plusieurs commandes? Cest là que la substitution de processus entre en jeu.

En dautres termes, la substitution de processus peut-elle faire tout ce que pipe peut faire?

Que peut faire la substitution de processus, mais que le tube ne peut pas faire?

Réponse

Une bonne façon de faire la différence entre eux est de faire un peu dexpérimentation sur la ligne de commande. En dépit de la similitude visuelle dans lutilisation du caractère <, il fait quelque chose de très différent dune redirection ou dun tube.

Utilisons le date commande de test.

$ date | cat Thu Jul 21 12:39:18 EEST 2011 

Cest un exemple inutile mais il montre que cat a accepté la sortie de date sur STDIN et la recrache. Les mêmes résultats peuvent être obtenus par substitution de processus:

$ cat <(date) Thu Jul 21 12:40:53 EEST 2011 

Cependant, ce qui venait de se passer dans les coulisses était différent. Au lieu de recevoir un flux STDIN, cat a reçu le nom dun fichier dont il avait besoin pour être ouvert et lisez. Vous pouvez voir cette étape en utilisant echo au lieu de cat.

$ echo <(date) /proc/self/fd/11 

Lorsque cat a reçu le nom du fichier, il a lu le contenu du fichier pour nous. Par contre, echo vient de nous montrer le nom du fichier qui lui a été transmis. Cette différence devient plus évidente si vous ajoutez plus de substitutions:

$ cat <(date) <(date) <(date) Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 Thu Jul 21 12:44:45 EEST 2011 $ echo <(date) <(date) <(date) /proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13 

Il est possible de combiner la substitution de processus (qui génère un fichier) et la redirection dentrée (qui connecte un fichier à STDIN):

$ cat < <(date) Thu Jul 21 12:46:22 EEST 2011 

Cela ressemble à peu près au même mais cette fois, cat a reçu un flux STDIN au lieu dun nom de fichier. Vous pouvez le voir en lessayant avec echo:

$ echo < <(date) <blank> 

Puisque echo ne lit pas STDIN et aucun argument na été passé, nous nobtenons rien.

Les tuyaux et les redirections dentrée poussent le contenu sur le flux STDIN. La substitution de processus exécute les commandes, enregistre leur sortie dans un fichier temporaire spécial, puis transmet ce nom de fichier à la place de la commande. La commande que vous utilisez la traite comme un nom de fichier. Notez que le fichier créé nest pas un fichier normal mais un tube nommé qui est automatiquement supprimé une fois quil nest plus nécessaire.

Commentaires

  • Si je correctement compris, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244 dit que la substitution de processus crée des fichiers temporaires, pas des tubes nommés. Autant que je sache, ne créez pas de fichiers temporaires. Lécriture sur le tube nimplique jamais décrire sur le disque: stackoverflow.com/a/6977599/788700
  • Je sais que cette réponse est légitime ‘ car il utilise le mot grok : D
  • @Adobe vous pouvez vérifier si la substitution de processus de fichier temporaire produit est un tube nommé avec: [[ -p <(date) ]] && echo true. Cela produit true lorsque je lexécute avec bash 4.4 ou 3.2.

Réponse

Voici trois choses que vous pouvez faire avec la substitution de processus qui sont impossibles autrement.

Entrées de processus multiples

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls) 

Là Il ny a tout simplement pas moyen de faire cela avec des tuyaux.

Préserver STDIN

Disons que vous avez ce qui suit:

curl -o - http://example.com/script.sh #/bin/bash read LINE echo "You said ${LINE}!" 

Et vous voulez lexécuter directement. Ce qui suit échoue lamentablement. Bash utilise déjà STDIN pour lire le script, donc toute autre entrée est impossible.

curl -o - http://example.com/script.sh | bash 

Mais cette méthode fonctionne parfaitement.

bash <(curl -o - http://example.com/script.sh) 

Substitution de processus sortant

Notez également que la substitution de processus fonctionne également dans lautre sens. Vous pouvez donc faire quelque chose comme ceci:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \ "/Permission denied/ s/.*\(\/proc.*\):.*/\1/p" > denied.txt ) 

Cest un peu un exemple compliqué, mais il envoie stdout à /dev/null, en envoyant stderr vers un script sed pour extraire les noms des fichiers pour lesquels une  » Autorisation refusée  » a été affichée, puis envoie CES résultats à un fichier.

Notez que la première commande et la redirection stdout sont entre parenthèses ( sous-shell ) de sorte que seul le résultat de CETTE commande soit envoyé à /dev/null et quil ne gâche pas le reste de la ligne.

Commentaires

  • Il ‘ est intéressant de noter que dans le diff exemple, vous voudrez peut-être vous soucier du cas où cd peut échouer: diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls).
  •  » pendant la tuyauterie stderr « : isn ‘ t le faire remarquer quil ne sagit pas de piping, mais de passer par un fichier fifo?
  • @Gauthier non; la commande est remplacée non pas par un fifo mais par une référence au descripteur de fichier. Donc  » echo < (echo)  » devrait donner quelque chose comme  » / dev / fd / 63 « , qui est un périphérique de caractères spéciaux qui lit ou écrit à partir du numéro FD 63.

Réponse

Je suppose que vous parlez de bash ou dun autre shell avancé, car le posix shell na pas de substitution de processus .

bash rapports de page de manuel:

Substitution de processus
La substitution de processus est prise en charge sur les systèmes qui prennent en charge les tubes nommés (FIFO) ou la méthode / dev / fd pour nommer les fichiers ouverts. Il prend la forme de < (liste) ou> (liste). La liste de processus est exécutée avec son entrée ou sa sortie connectée à un FIFO ou à un fichier dans / dev / fd. Le nom de ce fichier est passé en argument à la commande en cours à la suite de lexpansion. Si le formulaire> (liste) est utilisé, lécriture dans le fichier fournira une entrée pour la liste. Si le formulaire < (liste) est utilisé, le fichier passé en argument doit être lu pour obtenir la sortie de la liste.

Lorsquil est disponible, procéder à la substitution est effectuée simultanément avec le développement des paramètres et des variables, la substitution de commandes et le développement arithmétique.

En dautres termes, et dun point de vue pratique, vous pouvez utiliser une expression comme celle-ci

<(commands) 

comme nom de fichier pour dautres commandes nécessitant un fichier comme paramètre. Ou vous pouvez utiliser la redirection pour un tel fichier:

while read line; do something; done < <(commands) 

Revenant à votre question, il me semble que la substitution de processus et les tuyaux nont pas grand chose en commun.

Si vous souhaitez diriger en séquence la sortie de plusieurs commandes, vous pouvez utiliser lune des formes suivantes:

(command1; command2) | command3 { command1; command2; } | command3 

mais vous peut aussi utiliser la redirection lors de la substitution de processus

command3 < <(command1; command2) 

enfin, si command3 accepte un paramètre de fichier (en remplacement de stdin )

command3 <(command1; command2) 

Commentaires

  • so < ( ) et < < () fait le même effet, non?
  • @solfish: pas exacllty: le premier peut être utilisé partout où un nom de fichier est attendu, le second est une redirection dentrée pour ce nom de fichier

Réponse

Si un La commande prend une liste de fichiers comme arguments et traite ces fichiers comme entrée (ou sortie, mais pas couramment), chacun de ces fichiers peut être un tube nommé ou un pseudo-fichier / dev / fd fourni de manière transparente par la substitution de processus:

$ sort -m <(command1) <(command2) <(command3) 

Ceci « canalisera » la sortie des trois commandes à trier, car le tri peut prendre une liste de fichiers dentrée sur la ligne de commande.

Commentaires

  • IIRC, la syntaxe < (commande) est une fonctionnalité uniquement bash.
  • @Philomath: Elle ‘ est dans ZSH aussi.
  • Eh bien, ZSH a tout … (ou du moins essaie de le faire).
  • @Philomath: Comment la substitution de processus est-elle implémentée dans dautres shells?
  • @Philomath <(), comme de nombreuses fonctionnalités avancées du shell, était à lorigine une fonctionnalité ksh et a été adoptée par bash et zsh. psub est spécifiquement une fonction de poisson, rien à voir avec POSIX.

Réponse

Il convient de noter que la substitution de processus nest pas limitée à la forme <(command), qui utilise la sortie de command comme un déposer. Il peut être sous la forme >(command) qui alimente également un fichier en entrée de command. Ceci est également mentionné dans la citation du manuel bash dans la réponse de @enzotib « .

Pour lexemple date | cat ci-dessus, une commande qui utilise la substitution de processus du >(command) pour obtenir le même effet serait,

date > >(cat) 

Notez que > avant que >(cat) ne soit nécessaire. Cela peut à nouveau être clairement illustré par echo comme dans la réponse de @Caleb « .

$ echo >(cat) /dev/fd/63 

Donc, sans le supplément >, date >(cat) serait le identique à date /dev/fd/63 qui affichera un message sur stderr.

Supposons que vous ayez un programme qui ne prend que les noms de fichiers comme paramètres et ne traite pas stdin ou stdout.Jutiliserai le script simplifié à lextrême psub.sh pour illustrer cela. Le contenu de psub.sh est

#!/bin/bash [ -e "$1" -a -e "$2" ] && awk "{print $1}" "$1" > "$2" 

Fondamentalement, il teste que ses deux arguments sont des fichiers (pas nécessairement réguliers files) et si tel est le cas, écrivez le premier champ de chaque ligne de "$1" à "$2" en utilisant awk. Ensuite, une commande qui combine tout ce qui a été mentionné jusquà présent est,

./psub.sh <(printf "a a\nc c\nb b") >(sort) 

Cela affichera

a b c 

et équivaut à

printf "a a\nc c\nb b" | awk "{print $1}" | sort 

mais ce qui suit ne fonctionnera pas, et nous devons utiliser la substitution de processus ici,

printf "a a\nc c\nb b" | ./psub.sh | sort 

ou sa forme équivalente

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort 

Si ./psub.sh lit également stdin en plus de ce qui est mentionné ci-dessus, une telle forme équivalente nexiste pas, et dans ce cas, nous ne pouvons rien utiliser à la place de la substitution de processus (bien sûr, vous pouvez également utiliser un nommé pipe ou fichier temporaire, mais cest une autre histoire).

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *