Forum Linux.débutant Incompréhension des pipes sur Linux

Posté par  . Licence CC By‑SA.
Étiquettes :
2
26
août
2024

Bonsoir,

Quelqu'un peut-il m'expliquer pourquoi je n'obtiens pas le résultat attendu.
Je fais inévitablement une erreur de logique, sans comprendre laquelle…

$cat > fichier.txt
cool
pas cool
sympa
pas sympa
ctrl c

$tail -2 fichier.txt | nl fichier.txt
1 cool
2 pas cool
3 sympa
4 pas sympa

Résultat attendu :

$tail -2 fichier.txt | nl fichier.txt
3 sympa
4 pas sympa

Merci !

  • # logique...

    Posté par  (site web personnel) . Évalué à 10 (+9/-0). Dernière modification le 26 août 2024 à 22:15.

    $ tail -2 fichier.txt | nl fichier.txt

    essaie plutôt

    $ tail -2 fichier.txt | nl
  • # Qu'est-ce qu'un pipe ?

    Posté par  . Évalué à 10 (+11/-0). Dernière modification le 26 août 2024 à 23:44.

    Pour savoir exactement ce que ça fait, il faut savoir comment fonctionne un "tube", et connaitre le principe d'entrée et de sortie standard + erreur sous Unix.

    Tout processus lorsqu'il est créé possède par défaut trois descripteurs de fichiers ouverts qui lui serviront à communiquer : l'entrée standard (le clavier pour un processus interactif dans un shell), la sortie standard (le terminal par défaut), et la sortie d'erreur (le terminal également). Petit exemple : exécute la commande cat sans rien lui passer : tu verras qu'elle t'affichera tout ce que tu lui entres au clavier:

    $ cat
    abc
    abc
    def
    def
    ghi
    ghi
    
    

    Note : pour arrêter la saisie sur l'entrée standard, tu tapes sur les touches CTRL+D

    En fait la commande cat est une commande qui, si on ne lui spécifie pas de paramètres, va prendre tout ce qui arrive sur l'entrée standard pour l'afficher sur la sortie standard (petite parenthèse : le nom cat vient à l'origine du mot con*cat*, et cette commande permet d'afficher sur sa sortie standard les fichiers qu'on lui passe en paramètre).

    Le descripteur de fichier de l'entrée standard porte le numéro 0, celui de la sortie standard le numéro 1, et celui de la sortie d'erreur le numéro 2 (on y reviendra plus tard).

    Cependant, il est possible de faire des redirections des divers périphériques par défaut. Par exemple, tu peux exécuter la commade cat < /dev/random : en exécutant cette commande tu dis à cat que l'entrée standard n'est plus le clavier, mais le fichier que tu as passé en paramètre (ici /dev/randpm génère des nombres aléatoires : tu verras une bouillie binaire s'afficher à ton écran).

    De même qu'il est possible de changer le périphérique d'entrée, il est possible de changer le périphérique de sortie. Par exemple, si tu veux rediriger la sortie de la commande cat dans un fichier :

    totof@superbipbip:~$ cat >/tmp/toto.txt
    abc
    def
    ghi
    la vie est belle
    

    (taper control+d pour faire cesser la saisie)

    Là tu as généré un fichier que tu peux visualiser ainsi :

    totof@superbipbip:~$ more /tmp/toto.txt
    abc
    def
    ghi
    la vie est belle
    totof@superbipbip:~$ 
    

    Le troisième descripteur est la sortie d'erreur. Cette sortie affiche le message d'erreur. Faisons en sorte que cat génère une erreur en lui donnant un fichier bidon en paramètre d'entrée :

    totof@superbipbip:~$ cat /tmp/je_nexiste_pas
    cat: /tmp/je_nexiste_pas: Aucun fichier ou dossier de ce nom
    
    

    Une erreur qui s'affiche à l'écran, mais tu peux également la rediriger :

    totof@superbipbip:~$ cat /tmp/je_nexiste_pas 2>/tmp/erreur.txt
    

    La commande est "muette", mais elle génère bien un message d'erreur qu'on a redirigé vers un fichier

    totof@superbipbip:~$ more /tmp/erreur.txt 
    cat: /tmp/je_nexiste_pas: Aucun fichier ou dossier de ce nom
    
    

    Maintenant venons-en aux pipes. Quand tu enchaines les commandes via un pipe, tu demandes à ce que la sortie standard de la commande à gauche du pipe soit redirigée vers l'entrée standard de la commande à droite du pipe. Décomposons cet enchaînement :

    totof@superbipbip:~$ echo "coucou" | cat
    coucou
    

    Ici, la commande écho affiche sur la sortie standard la chaîne "coucou" et la commande cat la récupère depuis son entrée standard pour l'afficher à son tour sur sa sortie standard. A noter que les deux commandes tournent en parallèle. Le traitement de la commande de droite n'attend pas que la commande de gauche soit terminée pour s'exécuter. Elles tournent ensemble et les données sont traitées par la commande de droite au fur à mesure qu'elles arrivent de la commande de gauche.

    En redirigeant vers un fichier :

    totof@superbipbip:~$ echo "coucou" | cat >/tmp/coucou.txt
    totof@superbipbip:~$ more /tmp/coucou.txt 
    coucou
    

    Si tu veux récupérer la sortie standard et la sortie d'erreur pour la rediriger vers ton pipe tu peux : il suffit de le demander. Voici ce qui se passe sans redirection :

    totof@superbipbip:~$ cat /tmp/jenexistepas  | cat >/tmp/coucou.txt
    cat: /tmp/jenexistepas: Aucun fichier ou dossier de ce nom
    
    

    Le premier cat essaie de récpérer le contenu du fichier en paramètre, mais comme il n'existe pas, le processus affiche un message d'erreur. ( et si tu vas voir le contenu de /tmp/coucou.txt, tu auras un fichier vide car la commande n'a rien retourné sur sa sortie standard).

    Si on veut récupérer ce message d'erreur dans le pipe, il faut rediriger la sortie d'erreur (descripteur N° 2) vers la sortie standard (2>&1 permet de le faire dans la commande suivante) :

    totof@superbipbip:~$ cat /tmp/jenexistepas  2>&1 | cat >/tmp/coucou.txt
    

    Là encore, à la saisie on a l'impression qui ne se passe rien .. mais si on va voir :

    totof@superbipbip:~$ more /tmp/coucou.txt 
    cat: /tmp/jenexistepas: Aucun fichier ou dossier de ce nom
    

    Pour résumer, quand on exécute commmande 1 | commande 2, on demande à commande 2 de récupérer sur son entrée standard le contenu du canal de sortie standard de commande 1. Et si tu veux rediriger la sortie d'erreur vers le pipe, il faut que tu précises à la commande 1 (à gauche du pipe) de rediriger sa sortie d'erreur vers la sortie standard (je précise parce que c'est facile d'oublier).

    Maintenant pourquoi ce que tu as fait n'a pas fonctionné ?

    tail -2 fichier.txt | nl fichier.txt
    

    En gros tu dis à tail de récupérer les deux dernières lignes du fichier en paramètre. Tail les envoie sur sa sortie standard qui est capturée par le pipe. Sauf que à droite tu dis à la commande nl de ne pas récupérer la sortie standard, mais de récupérer les données du fichier passé en paramètre. Donc la sortie standard de la commande de gauche est ignorée. Tout ce que tu fais c'est exécuter des commandes en parallèle sans qu'elles n'interagissent entre elles.

    Maintenant un petit mot : j'ai utilisé cat pour l'exemple. Cependant beaucoup de monde croit que faire un cat est nécessaire pour pouvoir ensuite le traiter par une autre commande (tu verras beaucoup de cat fichier | grep par exemple). Le cat ne sert à rien dans ce cas. Démonstration avec le fichier généré précédemment :

    totof@superbipbip:~$ more /tmp/toto.txt
    abc
    def
    ghi
    la vie est belle
    

    Beaucoup de gens vont faire ceci :

    totof@superbipbip:~$ cat /tmp/toto.txt | grep def
    def
    
    

    Le cat ne sert à rien. On appelle ça UUOC ou Useless use of cat. Dans ce cas il vaut mieux faire ceci:

    totof@superbipbip:~$ grep def /tmp/toto.txt
    def
    
    

    Et si ta commande ne prend pas de fichier en paramètre mais ne prend ses données sur l'entrée standard (oui ça existe) :

    totof@superbipbip:~$ grep def < /tmp/toto.txt
    def
    
  • # explication

    Posté par  (Mastodon) . Évalué à 10 (+10/-0).

    Pour arriver à ton résultat attendu, il faut numéroter les lignes avant d'extraire la partie que tu souhaites voir :

    tth@redlady:~/Desktop$ cat > fichier.txt
    cool
    pas cool
    sympa
    pas sympa
    tth@redlady:~/Desktop$ nl fichier.txt | tail -2
         3  sympa
         4  pas sympa
    tth@redlady:~/Desktop$
    

    Et ne pas donner de nom de fichier à traiter à ta deuxième étape, pour qu'elle utilise son stdin (l'entrée standard) qui reçoit le résultat de la première étape.

  • # Petit détail

    Posté par  (Mastodon) . Évalué à 9 (+6/-0).

    Au tout début quand tu fais ton

    $cat > fichier.txt
    cool
    pas cool
    sympa
    pas sympa

    tu le finis par un violent CTRL-C (tu tues la commandes cat). tu devrais plutôt le finir par un CTRL-D qui est une fin de fichier qui dira donc à cat "c'est bon c'est fini !".

    perso c'est d'ailleurs avec un CTRL-D que je ferme mes terminaux ("fin des commandes").

    En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

  • # Normal, ...

    Posté par  (site web personnel, Mastodon) . Évalué à 4 (+4/-1).

    Ceci n'est pas une pipe

  • # Top merci

    Posté par  . Évalué à 1 (+0/-0).

    Bonsoir à tous,

    Ayant fait une petite pause dans mon apprentissage (vous l'aurez remarqué) de débutant. Sachez que j'étais de prime abord perdu…
    Je reviens faire un peu de pratique journalière cette semaine.

    J'ai pu apprécier énormément vos retours !
    J'ai tout compris, s
    Super explication totof2000. Je n'imaginais même pas que ce genre de chose était possible au sujet de la récupération d'erreur via le pipe (2>&1). D'ailleurs la syntaxe de ce genre de commande ne coule pas de source pour moi.. ou vous documentez vous pour ce genre de connaissance ?
    En tout cas, j'ai bien rigolé en sentant que j'avais tout compris.

    gUI, en me renseignant un peu plus, c'est vrai que c'est bourin ! Merci de ta remarque.

    • [^] # Re: Top merci

      Posté par  . Évalué à 2 (+1/-0). Dernière modification le 02 septembre 2024 à 06:14.

      Bonjour

      Il y a les pages du manuel du shell bash,
      et pour aller directement à la page du manuel concernant les redirections,
      tu peux lancer la ligne de commande suivante :

      man --pager='less -p "^REDIRECTIONS"' bash
      

      Concernant les pipes,
      la variable du shell bash PIPESTATUS pourrait aussi t'intéresser.

      man --pager='less -p "PIPESTATUS"' bash
      

      Voir aussi : Introduction à la programmation en Bash (Eric Sanchis)

      Un lien vers la page concernant les redirections : Chapitre 5. Redirections élémentaires

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.