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 ...