Por que o 'sed q' funciona de maneira diferente ao ler de um pipe?

24

Eu criei um arquivo de teste chamado 'test' que contém o seguinte:

xxx
yyy
zzz

eu corri o comando:

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

e eu recebi:

xxx
yyy
aaa
zzz

Então eu corri:

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

e obtivemos:

xxx
yyy
aaa

Pergunta

sed lê e imprime até encontrar uma linha com 'y' e depois pára. No primeiro caso, mas não no segundo, o gato lê e imprime o resto.

Alguém pode explicar que fenômeno está por trás dessa diferença de comportamento?

Eu também notei que funciona desta forma no Ubuntu 16.04 e no Centos 6, mas no Centos 7 nem manda imprimir 'zzz'.

    
por Antti Kuusela 22.08.2016 / 12:17

1 resposta

22

Quando o arquivo de entrada é buscável (como leitura de um arquivo normal) ou inutilizável (como leitura de um pipe), sed (e outros utilitários padrão) se comportará de maneira diferente (leia a seção INPUT FILES em este link ).

Citação do documento:

When a standard utility reads a seekable input file and terminates without an error before it reaches end-of-file, the utility shall ensure that the file offset in the open file description is properly positioned just past the last byte processed by the utility.

Então, em:

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

sed executou o comando q uit antes de atingir o EOF, então deixou o deslocamento do arquivo no início de zzz line, então cat pode continuar imprimindo as linhas restantes (o GNU sed não é compatível com POSIX em alguma condição, veja abaixo).

E continuando a partir do documento:

For files that are not seekable, the state of the file offset in the open file description for that file is unspecified

Nesse caso, o comportamento não é especificado. A maioria das ferramentas padrão, incluindo sed , consumirá a entrada o máximo possível. Ele leu passar a linha yyy e q sem restaurar o deslocamento do arquivo, portanto, nada é deixado para cat .

O GNU sed não é compatível com o padrão, depende da implementação do stdio do sistema e da versão glibc:

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

Aqui, o resultado foi obtido a partir do Mac OSX 10.11.6, máquinas virtuais Centos 7.2 - glibc 2.17, Ubuntu 14.04 - glibc 2.19, que são executados em Openstack com backend CEPH.

Nesses sistemas, você pode usar a opção -u para alcançar o comportamento padrão:

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

e para pipe:

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

que leva a um desempenho terrivelmente ineficiente, porque sed precisa ler um byte de cada vez. Uma saída parcial 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
...
    
por 22.08.2016 / 13:05

Tags