web-dev-qa-db-fra.com

commande "read" en attente d'un saut de ligne supplémentaire si l'entrée passe par le canal

Si je lance simplement read, il lit une ligne et se ferme immédiatement lorsque Enter est pressé.

$ read
typing something here
$

Cependant, lorsque je passe l’entrée à travers un tuyau, par ex. from cat, read se comporte différemment et continue à s'exécuter jusqu'à ce qu'il rencontre la deuxième nouvelle ligne:

$ cat | read
typing first line
typing second line
$

Quelqu'un peut-il expliquer pourquoi c'est comme ça?

PS: Cette question a été inspirée par Comment alimenter l’entrée standard et le vider dans un fichier en même temps?

3
Byte Commander

Cela n'a rien à voir avec la nouvelle ligne.

Si vous exécutez votre commande en utilisant strace, vous découvrirez que le cat recevra un SIGPIPE à la fin, avant d'être fermé:

$ strace cat | read

...
someOutput
...
+++ killed by SIGPIPE +++
  1. La première commande cat doit être exécutée.
  2. Ensuite, vous tapez quelque chose pour la première fois et appuyez sur la touche Enter.
  3. Ce que vous avez tapé sera transmis à la read.
  4. cat est toujours en cours d'exécution et attend un EOF.
  5. Vous tapez autre chose, puis appuyez sur la touche Enter agian.
  6. Cette fois, il ne peut pas être transmis à read, car il n'y a plus de read en attente d'entrée (il a été fermé après le premier tuyau) à moins que vous ne le lanciez comme ceci:

    cat | while read line; do echo $line; done;
    
  7. cat recevra un SIGPIPE et sera fermé.

Un processus reçoit un SIGPIPE lorsqu'il tente d'écrire dans un canal (nommé ou non) ou un socket de type SOCK_STREAM qui n'a plus de lecteur. [1]

La réception de SIGPIPE se produit après lorsque le second canal de communication se produit.

Par exemple, considérez la commande yes, car une commande telle que yes dirige quelque chose rapidement et de manière répétée:

yes | read

il se ferme immédiatement après le deuxième tuyau, faites attention aux deux appels write():

close(3)                                = 0                    
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 8192) = 8192          
write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 8192) = -1 EPIPE (Broken pipe) 
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=3542, si_uid=1000} ---
+++ killed by SIGPIPE +++

Cependant, étant donné que la commande yes est trop rapide, vous pouvez voir plus de deux appels write(), mais si vous l'exécutez plusieurs fois, vous ne verrez qu'au moins deux appels et jamais un.

2
Ravexina