Quelle est la différence entre “Redirection” et “Pipe”?

205

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 le 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 > greppour faire passer la sortie, n’est-ce pas aussi une sorte de redirection? Qu'est-ce qui me manque?

John Threepwood
la source

Réponses:

224

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

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

Exemple: thing1 > thing2vsthing1 | thing2

thing1 > thing2

  1. Votre shell exécutera le programme nommé thing1
  2. Tout ce qui thing1sort sera placé dans un fichier appelé thing2. (Remarque - s'il thing2existe, il sera écrasé)

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

thing1 > temp_file && thing2 < temp_file

qui ferait

  1. exécuter le programme nommé thing1
  2. enregistrer la sortie dans un fichier nommé temp_file
  3. exécutez le programme nommé thing2en prétendant que la personne au clavier a saisi le contenu de en temp_filetant qu'entrée.

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

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

Si vous >essayez 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 ailleurs dans le système (c'est-à-dire dans le chemin d'exécution)? 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 mal une commande, cela ne vous avertirait pas que la commande n'existe pas. En ce moment, si vous tapez, ls | gerp log.txtcela vous le dira bash: gerp: command not found. Si cela >signifiait les deux, il créerait simplement un nouveau fichier pour vous (alors avertissez-le, il ne sait pas quoi faire avec log.txt).

David Oneill
la source
Je vous remercie. Vous avez parlé thing1 > temp_file && thing2 < temp_filede faire plus facilement avec des pipes. Mais pourquoi ne pas réutiliser l' >opérateur pour le faire, par exemple thing1 > thing2pour les commandes thing1et thing2? Pourquoi un opérateur supplémentaire |?
John Threepwood
1
"Prendre la sortie et l'écrire dans un fichier" est une action différente de celle "Prendre la sortie et la transmettre à un programme différent". Je vais éditer plus de pensées dans ma réponse ...
David Oneill
1
@JohnThreepwood Ils ont des significations différentes. Et si je voulais rediriger quelque chose dans un fichier nommé less, par exemple? thing | lesset thing > lesssont parfaitement différents, comme ils font des choses différentes. Ce que vous proposez créerait une ambiguïté.
Darkhogg
Est-il exact de dire que "thing1> temp_file" n'est qu'un sucre syntaxique pour "thing1 | tee temp_file"? Depuis que j'ai découvert le tee, je n’utilise presque jamais de redirections.
Sridhar Sarnobat
2
@ Sridhar-Sarnobat non, la teecommande fait quelque chose de différent. teeécrit la sortie sur l’écran ( stdout) et le fichier. La redirection ne concerne que le fichier.
David Oneill
22

Si la signification de foo > bardépend de si une commande nommée barrend 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 mon fichier de destination.

Florian Diesch
la source
Cela ne poserait problème que si vous écrivez bardans un répertoire faisant partie de votre $PATHvariable env. Si vous êtes dans quelque chose comme / bin, alors ça pourrait être un problème. Mais même dans ce cas, il barfaudrait un ensemble d'autorisations exécutables, de sorte que le shell vérifie non seulement de trouver un exécutable, barmais peut également l'exécuter. Et si le problème concerne le remplacement du fichier existant, l’ nocloberoption shell devrait empêcher d’écraser les fichiers existants dans les redirections.
Sergiy Kolodyazhnyy
13

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

La redirection

Le shell interprète les symboles <,> et >> comme des instructions pour rediriger l' entrée ou la sortie d' une commande dans ou à partir d'un fichier .

Les pipes

Pour connecter le STDOUT d'une commande au STDIN d' une autre, utilisez la commande | 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.

Monsieur Peu importe
la source
12

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 à grep par le biais de pipe ( |), 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é pour rediriger la sortie vers un fichier.

Ankit
la source
3

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 réoriente comme ceci: cat [<infile] [>outfile]. Cela implique que l'ordre n'a pas d'importance: cat <infile >outfileest le même que cat >outfile <infile. Vous pouvez même mélanger les redirections avec d'autres arguments: cat >outfile <infile -bet cela va cat <infile -b >outfileparfaitement. Vous pouvez également enchaîner plus d'une entrée ou de sortie (entrées seront lues séquentiellement et toutes les sorties seront écrites à 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 depuis infile, puis écrit simultanément chaque ligne dans outfile et l'envoie à la seconde cat.

Dans la seconde cat, l’entrée standard lit d’abord dans le tuyau (le contenu d’infile), puis dans 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 une ligne à la fois pour grep, alors que la version canalisée prendra chaque ligne de ls au fur et à mesure, et passez cette ligne à grep.

utilisateur319857
la source
1
Nitpick: l’ordre importe dans la redirection si vous redirigez un fd vers un autre: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blahDe plus, la redirection vers un fichier n’utilisera que la dernière redirection. echo yes >/tmp/blah >/tmp/blah2va seulement écrire à /tmp/blah2.
muru
2
La redirection n'est pas réellement un argument pour le programme. Le programme ne saura pas ou ne se souciera pas de savoir où va sa sortie (ou l'entrée provient). C'est juste une façon de dire à bash comment arranger les choses avant d'exécuter le programme.
Alois Mahdal
3

Remarque: la réponse reflète ma propre compréhension de ces mécanismes actualisés, accumulés au cours de la recherche et de la lecture des réponses par les pairs sur ce site et sur unix.stackexchange.com , et sera mise à jour au fil du temps. 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 en shell avec stracecommande. Veuillez également ne pas être intimidé par la notion d'internes ou d'appels système - vous n'avez pas besoin de savoir ou de pouvoir les utiliser pour comprendre comment fonctionne un shell, mais ils aident vraiment à la compréhension.

TL; DR

  • |Les pipes ne sont pas associées à une entrée sur le disque, donc n’ont pas de numéro de système de fichiers sur le disque (mais ont un inode dans le système de fichiers virtuel pipefs dans l’espace noyau), mais les redirections impliquent souvent des fichiers qui ont des entrées de inode.
  • Les pipes ne sont pas lseek()'capables, donc les commandes ne peuvent pas lire certaines données et ensuite revenir en arrière, mais quand vous redirigez avec >ou <habituellement c'est un fichier qui est un lseek()objet capable, 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 nom de 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 dup2()appels système sous le capot pour fournir des copies des descripteurs de fichier, où le flux réel de données se produit.
  • Les redirections peuvent être appliquées "globalement" avec la execcommande intégrée (voir ceci et cela ), donc si vous le faites, exec > output.txtchaque commande écrit à output.txtpartir de là. |Les tuyaux ne sont appliqués que pour la commande en cours (ce qui signifie soit une commande simple, soit des commandes semblables à un sous-shell seq 5 | (head -n1; head -n2)ou composées.
  • Lorsque la redirection est effectuée sur des fichiers, des choses comme echo "TEST" > fileet echo "TEST" >> filetoutes les deux utilisent open()syscall sur ce fichier ( voir aussi ) et en tirent le descripteur de fichier dup2(). Les pipes |n'utilisent que 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; aptpar exemple, a tendance à ne même pas écrire sur stdout s'il sait qu'il y a 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, savoir ce que sont les descripteurs de fichier, comment dup2()et comment les pipe()appels système fonctionnent est essentiel 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 permet de comprendre la vraie nature du comportement de shell.

Les origines des redirections et des pipes

Selon l'article de Dennis Ritche, Prophetic Petroglyphs , les tuyaux proviennent d'une note interne de Malcolm Douglas McIlroy , datant de 1964 , à l'époque où ils travaillaient sur le 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 aussi la manière d’IO.

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 volumineuse. Pour citer l'explication de Brian Kernighan dans la vidéo Unix Pipeline :

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, rappelez-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 sous le capot, il se passe beaucoup de choses.

Aller plus loin: appels système et fonctionnement interne du shell

Nous commençons par la notion de descripteur de fichier . Les descripteurs de fichier décrivent essentiellement 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 respectivement les descripteurs de fichier 0,1 et 2. 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 les 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 indique 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. La définition standard de la redirection de sortie est

[n]>word

Qu'il [n]y ait le numéro de descripteur de fichier. Lorsque vous faites echo "Something" > /dev/nullle numéro 1 est implicite là, et echo 2> /dev/null.

Sous le capot, cela se fait en dupliquant le descripteur de fichier via dup2()un appel système. Prenons df > /dev/null. Le shell créera un processus enfant où dfs'exécutera, mais avant cela, il s'ouvrira en /dev/nulltant que descripteur de fichier n ° 3 et dup2(3,1)sera publié. Il créera une copie du descripteur de fichier 3 et la copie sera 1. Vous savez comment vous avez deux fichiers file1.txtet file2.txtet quand vous cp file1.txt file2.txtaurez deux fichiers identiques, mais que vous pourrez les manipuler indépendamment? C'est un peu la même chose qui se passe ici. Souvent, vous pouvez voir que, avant de lancer la tâche, bashil est dup(1,10)nécessaire de 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 les commandes intégrées(qui font partie du shell proprement dit, et n'ont aucun fichier /bindedans ou ailleurs) ou de simples commandes 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 à dup2()celui de la syntaxe du shell, 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 l’interprétation que shell donne à 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()etdup2()

Alors, comment les tuyaux sont-ils créés? Via pipe()syscall , qui prendra en entrée un tableau (ou liste) appelé pipefdde deux éléments de type int(entier). Ces deux entiers sont des descripteurs de fichier. Le pipefd[0]sera l'extrémité de lecture du tuyau et pipefd[1]sera l'extrémité d'écriture. So in df | grep 'foo', grepobtiendra une copie pipefd[0]et dfobtiendra une copie de pipefd[1]. Mais comment ? Bien sûr, avec la magie de l' dup2()appel système. Par dfexemple, dans notre exemple, pipefd[1]a # 4, le shell fera un enfant, do dup2(4,1)(souvenez-vous de mon cpexemple?), Puis execve()exécutez df. Naturellement,dfhéritera du descripteur de fichier n ° 1, mais ne saura pas qu'il ne pointe plus sur le terminal, mais sur le fichier fd n ° 4, qui correspond en réalité à la fin du tuyau. Naturellement, la même chose se produira grep 'foo'sauf avec des nombres différents de descripteurs de fichiers.

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 qui se |&passe à Bash. La norme POSIX exige que le langage de commande shell prenne en charge la df 2>&1 | grep 'foo'syntaxe à cette fin, mais il en bashva |&de même.

Il est important de noter que les pipes traitent toujours avec des descripteurs de fichier. Il existe FIFOou pipe nommée , qui a un nom de fichier sur le disque et vous permet de l'utiliser comme un fichier, mais se comporte comme une pipe. Mais les |types de canaux sont ce qu’on appelle des tubes anonymes - ils n’ont pas de nom de fichier, car ce ne sont en réalité que deux objets reliés entre eux. Le fait que nous n'ayons pas affaire à des fichiers a également une implication importante: les pipes ne sont pas lseek()capables. Les fichiers, en mémoire ou sur disque, sont statiques - les programmes peuvent utiliser lseek()syscall pour passer à l'octet 120, puis revenir à l'octet 10, puis le transférer à la fin. Les pipes ne sont pas statiques - elles sont séquentielles, et vous ne pouvez donc pas rembobiner les données que vous obtenez aveclseek(). C’est ce qui permet à certains programmes de savoir s’ils lisent à partir d’un fichier ou d’un tuyau, et peuvent donc effectuer les ajustements nécessaires pour obtenir des performances efficaces; en d'autres termes, un progpeut détecter si je le fais cat file.txt | progou prog < input.txt. Le vrai exemple de travail est la queue .

Les deux autres propriétés très intéressantes des pipes sont qu’elles ont un tampon, ce qui sous Linux est de 4096 octets , et qu’elles ont 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, comme il existe un système de fichiers pipefs, qui gère à la fois les tubes et les FIFO, les tubes ont un numéro inode 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 également créer des pipes bidirectionnelles sur Linux .

Voir également:

Sergiy Kolodyazhnyy
la source
2

Pour ajouter aux autres réponses, il existe également une différence sémantique subtile - par exemple, 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 à headterminer se termine, il ferme le tuyau et se seqtermine, il n'y a donc aucune entrée disponible pour le second head.

Dans le second exemple, head utilise la première ligne, mais lorsqu'il ferme son propre stdin canal , le fichier reste ouvert pour le prochain appel à utiliser.

Le troisième exemple montre que si nous utilisons readpour é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.

PS Si vous êtes aussi curieux et / ou surpris par ces exemples que moi, vous pouvez aller plus loin en trapvous montrant 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 d' 1être imprimé, parfois après.

J'ai aussi trouvé intéressant de exec <&-fermer le flux de la redirection pour se rapprocher du comportement du canal (avec une erreur toutefois):

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`
Julian de Bhal
la source
"quand le premier appel à la tête se termine, il ferme le tuyau" Ceci est en fait inexact pour deux raisons. L'un, (head -n1; head -n1) est un sous-shell avec deux commandes, chacune héritant de la fin de lecture d'un canal comme descripteur 0, et ainsi, le sous-shell ET chaque commande ont ce descripteur de fichier ouvert. Deuxième raison, vous pouvez voir que avec strace -f bash -c 'seq 5 | (tête -n1; tête -n1) '. Donc, la première tête ferme seulement sa copie du descripteur de fichier
Sergiy Kolodyazhnyy
Le troisième exemple est également inexact, car readconsomme uniquement la première ligne (c'est un octet pour 1et une nouvelle ligne). seqenvoyé au total 10 octets (5 numéros et 5 nouvelles lignes). Il reste donc 8 octets dans le tampon de canal, et c’est pourquoi second headfonctionne: il reste des données dans le tampon de canal. Au fait, la tête ne sort que s'il y a 0 octets lus, un peu comme danshead /dev/null
Sergiy Kolodyazhnyy
Merci pour la clarification. Ai-je bien compris que lors seq 5 | (head -n1; head -n1)du premier appel, le canal est vidé, de sorte qu'il existe toujours à l'état ouvert, mais sans données pour le deuxième appel à head? La différence de comportement entre le tube et la redirection est donc due au fait que head extrait toutes les données du tube, mais uniquement les 2 lignes du fichier.
Julian de Bhal
C'est correct. Et c'est quelque chose que l'on peut voir avec la stracecommande que j'ai donnée dans le premier commentaire. Avec la redirection, le fichier tmp se trouve sur le disque, ce qui le rend utilisable (car ils utilisent lseek()syscall - les commandes permettent de contourner le fichier du premier au dernier octet comme bon leur semble. Mais les canaux sont séquentiels et non recherchables. Le travail consiste à tout lire en premier, ou si le fichier est gros - en mapper une partie à la RAM via un mmap()appel. J'ai déjà fait le mien tailen Python et j'ai rencontré exactement le même problème.
Sergiy Kolodyazhnyy
Il est également important de se rappeler que la fin de lecture du tuyau (descripteur de fichier) est d'abord attribuée au sous (...)-shell, qui effectuera une copie de son propre stdin pour chaque commande qu'il contient (...). Donc, techniquement, ils sont lus à partir du même objet. head Pense d' abord qu'il lit à partir de son propre stdin. Second headpense avoir son propre stdin. Mais en réalité, leur fd # 1 (stdin) est une copie du même fd, qui est lu à la fin du tube. De plus, j'ai posté une réponse, alors peut-être que ça va aider à clarifier les choses.
Sergiy Kolodyazhnyy
1

J'ai rencontré un problème avec cela en C aujourd'hui. Essentiellement, les tuyaux ont une sémantique différente vers les redirections, même lorsqu'ils sont envoyés à stdin. Vraiment , je pense qu'étant donné les différences, les tuyaux doivent aller quelque part ailleurs que stdin, de sorte que stdinet laisse l' appeler stdpipe(pour faire un différentiel arbitraire) peuvent être traitées de différentes façons.

Considère ceci. Lorsque la tuyauterie d' une sortie de programme à l' autre fstatsemble revenir zéro comme st_sizemalgré ls -lha /proc/{PID}/fdmontre qu'il ya un fichier. Lors de la redirection d'un fichier, ce n'est pas le cas (du moins sur debian wheezy, stretchet jessievanilla et ubuntu 14.04, 16.04vanilla.

Si vous cat /proc/{PID}/fd/0utilisez 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.

MrMesees
la source