Pourquoi sed q fonctionne-t-il différemment lors de la lecture d'un tube?

J'ai créé un file de test nommé 'test' qui contient les éléments suivants:

xxx yyy zzz 

J'ai couru la command:

 (sed '/y/ q'; echo aaa; cat) < test 

et j'ai eu:

 xxx yyy aaa zzz 

Puis j'ai couru:

 cat test | (sed '/y/ q'; echo aaa; cat) 

et a obtenu:

 xxx yyy aaa 

Question

sed lit et imprime jusqu'à ce qu'il rencontre une ligne avec «y», puis s'arrête. Dans le premier cas, mais pas le second, le chat lit et imprime le rest.

Quelqu'un peut-il expliquer quel phénomène est à l'origine de cette différence de comportement?

J'ai aussi remarqué que cela fonctionne de la même façon dans Ubuntu 16.04 et Centos 6, mais dans Centos 7, aucune command n'imprime "zzz".

Lorsque le file d'input est recherché (comme la lecture à partir d'un file normal) ou irrécupérable (comme la lecture d'un tube), sed (et d'autres utilitaires standard) se comportera différemment.

Citation du doc:

Lorsqu'un utilitaire standard lit un file d'input recherché et se termine sans erreur avant qu'il n'atteigne la fin du file, l'utilitaire doit s'assurer que le décalage du file dans la description du file ouvert est correctement positionné juste après le dernier octet traité par l'utilitaire.

Donc dans:

 (sed '/y/ q'; echo aaa; cat) < test 

sed exécuté la command q uit avant d'atteindre EOF, donc il a laissé le décalage du file au début de la ligne zzz , afin que cat puisse continuer à imprimer les lignes restantes.

Et continuant depuis le doc:

Pour les files qui ne peuvent pas être recherchés, l'état du file offset dans la description du file ouvert pour ce file n'est pas spécifié

Dans ce cas, le comportement n'est pas spécifié. La plupart des outils standard, y compris sed , consumront l'input autant que possible. Il lit passer la ligne yyy et q uit sans restaurer le décalage du file, il ne rest plus rien pour cat .


GNU sed n'est pas conforme à la norme, dépend de l'implémentation stdio du système et de la version glibc:

 $ (gsed '/y/ q'; echo aaa; cat) < test xxx yyy aaa 

Ici, le résultat a été obtenu à partir de Mac OSX 10.11.6, machines virtuelles Centos 7.2 – glibc 2.17, Ubuntu 14.04 – glibc 2.19, qui sont exécutés sur Openstack avec CEPH backend.

Sur ces systèmes, vous pouvez utiliser l'option -u pour get le comportement standard:

 (gsed -u '/y/ q'; echo aaa; cat) </tmp/test 

et pour la pipe:

 $ cat test | (gsed -u '/y/ q'; echo aaa; cat) xxx yyy aaa zzz 

ce qui conduit à des performances terriblement inefficaces, car sed doit lire un octet à la fois. Une sortie partielle de strace :

 $ strace -fe read sh -c '{ sed -u "/y/q"; echo aaa; cat; } <test' ... [pid 5248] read(3, "", 4096) = 0 [pid 5248] read(0, "x", 1) = 1 [pid 5248] read(0, "x", 1) = 1 [pid 5248] read(0, "x", 1) = 1 [pid 5248] read(0, "\n", 1) = 1 xxx [pid 5248] read(0, "y", 1) = 1 [pid 5248] read(0, "y", 1) = 1 [pid 5248] read(0, "y", 1) = 1 [pid 5248] read(0, "\n", 1) = 1 yyy ...