web-dev-qa-db-fra.com

Quelle est la différence entre "Redirection" et "Pipe"?

Cette question peut sembler un peu stupide, mais je ne vois pas vraiment la différence entre redirection et pipes.

La redirection est utilisée pour rediriger stdout/stdin/stderr, par exemple. ls > log.txt.

Les pipes sont utilisées pour donner le résultat d'une commande en entrée d'une autre commande, par exemple. ls | grep file.txt.

Mais pourquoi y a-t-il deux opérateurs pour la même chose?

Pourquoi ne pas simplement écrire ls > grep pour transmettre le résultat, n’est-ce pas aussi une sorte de redirection? Qu'est-ce qui me manque?

192
John Threepwood

Le tuyau est utilisé pour passer la sortie à un autre programme ou utilitaire.

La redirection est utilisée pour transmettre la sortie à un fichier ou flux.

Exemple: thing1 > thing2 vs thing1 | thing2

thing1 > thing2

  1. Votre shell exécutera le programme nommé thing1
  2. Tout ce que thing1 produira sera placé dans un fichier nommé thing2. (Remarque - si thing2 existe, il sera écrasé)

Si vous souhaitez transmettre le résultat du programme thing1 à un programme appelé thing2, vous pouvez procéder comme suit:

thing1 > temp_file && thing2 < temp_file

qui ferait

  1. lancez le programme nommé thing1
  2. enregistrer la sortie dans un fichier nommé temp_file
  3. exécutez le programme nommé thing2, en prétendant que la personne au clavier a saisi le contenu de temp_file comme entrée.

Cependant, c'est maladroit, alors ils ont fabriqué des pipes comme un moyen plus simple de le faire. thing1 | thing2 fait la même chose que thing1 > temp_file && thing2 < temp_file

EDIT pour fournir plus de détails à la question dans le commentaire:

Si > essayait d'être à la fois "passer au programme" et "écrire dans un fichier", cela pourrait poser des problèmes dans les deux sens.

Premier exemple: Vous essayez d'écrire dans un fichier. Il existe déjà un fichier portant ce nom que vous souhaitez écraser. Cependant, le fichier est exécutable. Vraisemblablement, il essaierait d'exécuter ce fichier en transmettant l'entrée. Vous devez faire quelque chose comme écrire la sortie dans un nouveau nom de fichier, puis renommer le fichier.

Deuxième exemple: Comme Florian Diesch l'a fait remarquer, que se passe-t-il s'il existe une autre commande du même nom (c'est-à-dire dans le chemin d'exécution) ailleurs dans le système? Si vous aviez l'intention de créer un fichier portant ce nom dans votre dossier actuel, vous seriez bloqué.

Troisièmement: si vous tapez une commande de manière incorrecte, cela ne vous avertirait pas que la commande n'existe pas. Pour le moment, si vous tapez ls | gerp log.txt, bash: gerp: command not found vous le dira. Si > signifiait les deux, il créerait simplement un nouveau fichier pour vous (alors avertissez-le, il ne sait pas quoi faire avec log.txt).

215
David Oneill

Si la signification de foo > bar dépend de la présence d'une commande nommée bar qui rendrait l'utilisation de la redirection beaucoup plus difficile et plus sujette aux erreurs: chaque fois que je veux rediriger vers un fichier, je dois d'abord vérifier s'il existe une commande nommée comme ma destination fichier.

22
Florian Diesch

Du manuel d’administration du système Unix et Linux:

La redirection

Le shell interprète les symboles <,> et >> comme des instructions pour rediriger une entrée commande ou une sortie vers ou depuis un fichier.

Les pipes

Pour connecter le STDOUT d'un commande au STDIN de n autre, utilisez le | symbole, communément appelé tuyau.

Donc, mon interprétation est la suivante: si commande à commande, utilisez un tuyau. Si vous exportez depuis ou vers un fichier, utilisez la redirection.

12
Mr Whatever

Il y a une différence vitale entre les deux opérateurs:

  1. ls > log.txt -> Cette commande envoie la sortie au fichier log.txt.

  2. ls | grep file.txt -> Cette commande envoie le résultat de la commande ls to grep par l’intermédiaire du canal (|), et la commande grep recherche file.txt dans l’entrée fournie par la commande précédente.

Si vous deviez effectuer la même tâche en utilisant le premier scénario, alors ce serait:

ls > log.txt; grep 'file.txt' log.txt

Ainsi, un tuyau (avec |) est utilisé pour envoyer la sortie à une autre commande, alors que la redirection (avec >) est utilisée pour rediriger la sortie vers un fichier.

12
Ankit

Remarque: la réponse reflète ma propre compréhension de ces mécanismes à jour accumulés au fil des recherches et de la lecture des réponses par les pairs sur ce site et nix.stackexchange.com , et sera mise à jour au fil du temps. sur. N'hésitez pas à poser des questions ou à suggérer des améliorations dans les commentaires. Je vous suggère également d'essayer de voir comment les appels système fonctionnent dans Shell avec la commande strace. Veuillez également ne pas être intimidé par la notion d'internes ou d'appels système - vous n'avez pas besoin de savoir ni de pouvoir les utiliser pour comprendre comment Shell fait les choses, mais ils aident vraiment à la compréhension.

TL; DR

  • | tubes ne sont pas associés à une entrée sur le disque, donc n’ont pas d’inode nombre de système de fichiers disque (mais ont un inode dans pipefs système de fichiers virtuel dans l’espace noyau), mais les redirections impliquent souvent des fichiers, qui ont des entrées de disque et donc un inode correspondant.
  • les tubes ne sont pas lseek() 'capables, les commandes ne peuvent donc pas lire certaines données et ensuite revenir en arrière, mais lorsque vous redirigez avec > ou <, il s'agit généralement d'un fichier qui est un objet lseek(), les commandes peuvent donc naviguer comme bon leur semble.
  • les redirections sont des manipulations sur les descripteurs de fichiers, qui peuvent être nombreux; les pipes ont seulement deux descripteurs de fichier - un pour la commande gauche et un pour la commande droite
  • la redirection sur les flux standard et les pipes sont tous deux tamponnés.
  • les tuyaux impliquent presque toujours un forgeage et par conséquent des paires de processus sont impliqués; redirections - pas toujours, bien que dans les deux cas les descripteurs de fichiers résultants soient hérités par des sous-processus.
  • les pipes connectent toujours des descripteurs de fichier (une paire), des redirections - utilisez un chemin ou des descripteurs de fichier.
  • les tubes sont une méthode de communication interprocessus, tandis que les redirections ne sont que des manipulations sur des fichiers ouverts ou des objets de type fichier
  • les deux utilisent des appels système dup2() sous le capot pour fournir des copies des descripteurs de fichier, lorsque le flux réel de données se produit.
  • les redirections peuvent être appliquées "globalement" avec la commande intégrée exec (voir this et this ), donc si vous faites exec > output.txt chaque commande écrira ensuite à output.txt. Les tubes | sont appliqués uniquement à la commande en cours (ce qui signifie une commande simple ou un sous-shell comme seq 5 | (head -n1; head -n2) ou des commandes composées.
  • Lorsque la redirection est effectuée sur des fichiers, des éléments tels que echo "TEST" > file et echo "TEST" >> file utilisent tous les deux open() syscall sur ce fichier ( voir aussi ) et en obtiennent le descripteur de fichier pour le transmettre à dup2(). Les tubes | utilisent uniquement pipe() et dup2() syscall.

  • En ce qui concerne les commandes en cours d'exécution, les canaux et la redirection ne sont rien de plus que des descripteurs de fichier - des objets de type fichier, sur lesquels ils peuvent écrire en aveugle, ou les manipuler en interne (ce qui peut produire des comportements inattendus; apt par exemple, tend à même pas écrire sur stdout s'il sait qu'il existe une redirection).

Introduction

Afin de comprendre en quoi ces deux mécanismes diffèrent, il est nécessaire de comprendre leurs propriétés essentielles, leur histoire et leurs racines dans le langage de programmation C. En fait, connaître les descripteurs de fichier et savoir comment fonctionnent les appels système dup2() et pipe() est essentiel, de même que lseek(). Shell est conçu comme un moyen de rendre ces mécanismes abstraits pour l'utilisateur, mais creuser plus profondément que l'abstraction aide à comprendre la vraie nature du comportement de Shell.

Les origines des redirections et des pipes

Selon l'article de Dennis Ritche Pétroglyphes prophétiques , les pipes provenaient d'un mémo interne de 1964 de Malcolm Douglas McIlroy , à l'époque où ils travaillaient - Système d'exploitation Multics . Citation:

Pour résumer mes plus grandes préoccupations:

  1. Nous devrions avoir des moyens de connecter des programmes tels que tuyau d’arrosage - vissez un autre segment quand il devient nécessaire de masser les données d’une autre manière. C'est la manière de IO également.

Ce qui est évident, c’est qu’à l’époque, les programmes étaient capables d’écrire sur disque, mais c’était inefficace si la sortie était importante. Pour citer l'explication de Brian Kernighan dans nix Pipeline video:

Premièrement, vous n’avez pas à écrire un gros programme volumineux - vous avez des programmes plus petits existants qui font peut-être déjà une partie du travail ... Un autre problème est que la quantité de données que vous traitez ne correspondrait pas si vous l'avez stocké dans un fichier ... parce que, souvenez-vous, nous étions de retour à l'époque où les disques contenant ces choses avaient, si vous aviez de la chance, un ou plusieurs mégaoctets de données ... Ainsi, le pipeline n'a jamais eu à instancier l'intégralité de la sortie. .

La différence conceptuelle est donc évidente: les pipes sont un mécanisme permettant aux programmes de se parler. Les redirections - sont une façon d'écrire dans un fichier au niveau de base. Dans les deux cas, Shell facilite ces deux choses, mais il y a beaucoup de choses qui se passent sous le capot.

Approfondissement: appels système et fonctionnement interne de Shell

Nous commençons par la notion de descripteur de fichier . Les descripteurs de fichier décrivent en gros un fichier ouvert (qu'il s'agisse d'un fichier sur disque, en mémoire ou anonyme), qui est représenté par un nombre entier. Les deux flux de données standard (stdin, stdout, stderr) sont les descripteurs de fichier 0,1 et 2 respectivement. D'où viennent-ils ? Dans les commandes Shell, les descripteurs de fichier sont hérités de leur parent, Shell. Et c'est vrai en général pour tous les processus - le processus enfant hérite des descripteurs de fichier du parent. Pour démons , il est courant de fermer tous les descripteurs de fichiers hérités et/ou de les rediriger vers d'autres emplacements.

Retour à la redirection. C'est quoi vraiment? C'est un mécanisme qui demande au shell de préparer les descripteurs de fichier pour la commande (car les redirections sont effectuées par le shell avant l'exécution de la commande) et de les indiquer à l'endroit suggéré par l'utilisateur. Le définition standard de la redirection de sortie est

[n]>Word

Ce [n] il y a le numéro du descripteur de fichier. Lorsque vous faites echo "Something" > /dev/null le numéro 1 est impliqué, et echo 2> /dev/null.

Sous le capot, cela se fait en dupliquant le descripteur de fichier via l'appel système dup2(). Prenons df > /dev/null. Le shell créera un processus enfant dans lequel df sera exécuté, mais avant cela, il ouvrira /dev/null en tant que descripteur de fichier n ° 3, et dup2(3,1) sera émis, ce qui créera une copie du descripteur de fichier 3 et la copie sera 1. Vous savez comment vous avez deux fichiers file1.txt et file2.txt, et lorsque vous ferez cp file1.txt file2.txt, vous aurez deux mêmes fichiers, mais vous pouvez les manipuler indépendamment? C'est un peu la même chose qui se passe ici. Souvent, vous pouvez voir qu'avant l'exécution, bash fera dup(1,10) pour créer un descripteur de fichier de copie n ° 1 qui est stdout (et que cette copie sera le disque n ° 10) afin de le restaurer ultérieurement. Il est important de noter que lorsque vous considérez commandes intégrées (qui font partie de Shell et ne possèdent aucun fichier dans /bin ou ailleurs) ou commandes simples dans un Shell non interactif =, le shell ne crée pas de processus enfant.

Et puis nous avons des choses comme [n]>&[m] et [n]&<[m]. Cela duplique les descripteurs de fichier, dont le mécanisme est identique à celui de dup2(), mais il est maintenant dans la syntaxe du shell, facilement disponible pour l'utilisateur.

Un des points importants à noter à propos de la redirection est que leur ordre n'est pas fixe, mais qu'il est important pour Shell d'interpréter ce que l'utilisateur souhaite. Comparez ce qui suit:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

L'utilisation pratique de ceux-ci dans les scripts Shell peut être polyvalente:

et beaucoup d'autres.

Plomberie avec pipe() et dup2()

Alors, comment les tuyaux sont-ils créés? Via pipe() syscall , qui prendra en entrée un tableau (ou liste) appelé pipefd de deux éléments de type int (entier). Ces deux entiers sont des descripteurs de fichier. pipefd[0] sera la fin de lecture du tube et pipefd[1] sera la fin d'écriture. Donc, dans df | grep 'foo', grep recevra une copie de pipefd[0] et df recevra une copie de pipefd[1]. Mais comment ? Bien sûr, avec la magie de dup2() syscall. Pour df dans notre exemple, supposons que pipefd[1] ait le numéro 4, de sorte que le shell créera un enfant, fera dup2(4,1) (souvenez-vous de mon exemple cp?), Puis execve() pour exécuter df. Naturellement, df héritera du descripteur de fichier n ° 1, mais ne saura pas qu'il ne pointe plus sur le terminal, mais bien sur fd n ° 4, qui correspond en réalité à l'extrémité d'écriture du canal. Naturellement, la même chose se produira avec grep 'foo' sauf que le nombre de descripteurs de fichier sera différent.

Maintenant, question intéressante: pourrions-nous aussi créer des pipes qui redirigeront fd # 2, pas seulement fd # 1? Oui, en fait, c'est ce que |& fait dans bash. La norme POSIX exige que le langage de commande Shell prenne en charge la syntaxe df 2>&1 | grep 'foo' à cette fin, mais bash le fait également à |&.

Il est important de noter que les pipes traitent toujours avec des descripteurs de fichier. Il existe FIFO ou tube nommé , qui a un nom de fichier sur le disque et vous permet de l'utiliser comme un fichier, mais se comporte comme un tube. Mais les types de canaux | sont ce que l’on appelle des tubes anonymes - ils n’ont pas de nom de fichier, car ce ne sont en réalité que deux objets connectés ensemble. Le fait que nous n'ayons pas affaire à des fichiers implique également une importante implication: les pipes ne sont pas lseek() 'capable. Les fichiers, en mémoire ou sur disque, sont statiques - les programmes peuvent utiliser lseek() syscall pour sauter à l'octet 120, puis à l'octet 10, puis avancez jusqu'au bout. Les pipes ne sont pas statiques - elles sont séquentielles et vous ne pouvez donc pas rembobiner les données que vous obtenez avec lseek(). C’est ce qui permet à certains programmes de savoir s’ils lisent à partir de fichiers ou de tuyaux, et peuvent donc effectuer les ajustements nécessaires pour obtenir des performances optimales; En d'autres termes, un prog peut détecter si je fais cat file.txt | prog ou prog < input.txt. Exemple de travail réel de cela est queue .

Les deux autres propriétés très intéressantes des pipes sont qu’elles ont un tampon, qui sous Linux est de 4096 octets , et qu’elles ont en fait un système de fichiers tel que défini dans le code source de Linux ! Ils ne sont pas simplement un objet de transmission de données, ils sont eux-mêmes une structure de données! En fait, puisqu'il existe un système de fichiers pipefs, qui gère à la fois les tubes et les FIFO, les tubes ont un inode numéro sur leur système de fichiers respectif:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

Sur Linux, les canaux sont unidirectionnels, tout comme la redirection. Sur certaines implémentations de type Unix, il existe des tuyaux bidirectionnels. Bien qu'avec la magie des scripts Shell, vous pouvez aussi faire pipes bidirectionnelles sous Linux .

Voir également:

3
Sergiy Kolodyazhnyy

Il y a une grande différence syntaxique entre les deux:

  1. Une redirection est un argument pour un programme
  2. Un tuyau sépare deux commandes

Vous pouvez penser à des redirections comme ceci: cat [<infile] [>outfile]. Cela implique que l'ordre n'a pas d'importance: cat <infile >outfile est identique à cat >outfile <infile. Vous pouvez même mélanger les redirections avec d'autres arguments: cat >outfile <infile -b et cat <infile -b >outfile conviennent parfaitement. Vous pouvez également chaîner plusieurs entrées ou sorties (les entrées seront lues de manière séquentielle et toutes les sorties seront écrites dans chaque fichier de sortie): cat >outfile1 >outfile2 <infile1 <infile2. La cible ou la source d'une redirection peut être un nom de fichier ou le nom d'un flux (comme & 1, au moins en bash).

Mais les tubes séparent totalement une commande d'une autre, vous ne pouvez pas les mélanger avec des arguments:

[command1] | [command2]

Le tube prend tout ce qui est écrit dans la sortie standard de commande1 et l'envoie à l'entrée standard de commande2.

Vous pouvez également combiner la tuyauterie et la redirection. Par exemple:

cat <infile >outfile | cat <infile2 >outfile2

Le premier catlit les lignes de infile, puis écrit simultanément chaque ligne dans outfile et les envoie au second catname__.

Dans la deuxième catname__, l’entrée standard lit d’abord dans le tube (le contenu d’infile), puis à partir de infile2, en écrivant chaque ligne dans outfile2. Après avoir exécuté ceci, outfile sera une copie de infile, et outfile2 contiendra infile suivi de infile2.

Enfin, vous faites quelque chose de vraiment similaire à votre exemple en utilisant la redirection "here string" (famille bash uniquement) et des backticks:

grep blah <<<`ls`

donnera le même résultat que

ls | grep blah

Mais je pense que la version de redirection lira d’abord toute la sortie de ls dans un tampon (en mémoire), puis alimentera ce tampon dans grep une ligne à la fois, alors que la version en mode pipeline prendra chaque ligne de ls au fur et à mesure, et passez cette ligne à grep.

3
user319857

Pour ajouter aux autres réponses, il existe également une différence sémantique subtile - par ex. les tuyaux se ferment plus facilement que les redirections:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

Dans le premier exemple, lorsque le premier appel à head est terminé, il ferme le canal et seq, de sorte qu'il n'y a aucune entrée disponible pour le second head.

Dans le deuxième exemple, head utilise la première ligne, mais lorsqu'il ferme son propre nom stdinpipe, le fichier reste ouvert pour le prochain appel à utiliser.

Le troisième exemple montre que si nous utilisons read pour éviter de fermer le canal, celui-ci est toujours disponible dans le sous-processus.

Donc, le "flux" est la chose par laquelle nous shuntons des données (stdin, etc.), et est identique dans les deux cas, mais le canal connecte les flux de deux processus, où une redirection connecte un flux entre un processus et un fichier. peut voir la source des similitudes et des différences.

P.S. Si vous êtes aussi curieux et/ou surpris par ces exemples que moi, vous pouvez aller plus loin dans Dig en utilisant trap pour voir comment les processus se résolvent, par exemple:

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

Parfois, le premier processus se ferme avant que 1 soit imprimé, parfois plus tard.

J'ai également trouvé intéressant d'utiliser exec <&- pour fermer le flux de la redirection afin d'approcher le comportement du canal (bien qu'avec une erreur):

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`
2
Julian de Bhal

J'ai rencontré un problème avec cela en C aujourd'hui. La sémantique des redirections est également différente pour les tuyaux, même lorsqu'ils sont envoyés à stdinname__. Vraiment, étant donné les différences, les pipes devraient aller ailleurs que stdinname__, de sorte que stdinet qu'on l'appelle stdpipe(pour créer un différentiel arbitraire) puisse être traité de différentes manières.

Considère ceci. Lorsque vous redirigez la sortie d'un programme vers un autre fstatsemble renvoyer zéro en tant que st_size malgré ls -lha /proc/{PID}/fd montrant qu'il existe un fichier. Lors de la redirection d'un fichier, ce n'est pas le cas (du moins sur debian wheezyname__, stretchet jessieVanilla et ubuntu 14.04, 16.04Vanilla.

Si vous cat /proc/{PID}/fd/0 avec une redirection, vous pourrez répéter la lecture autant de fois que vous le souhaitez. Si vous faites cela avec un tuyau, vous remarquerez que la deuxième fois que vous exécutez la tâche consécutivement, vous n'obtenez pas le même résultat.

1
MrMesees