Não entendo como os dados fluem no pipeline e espero que alguém possa esclarecer o que está acontecendo lá.
Eu pensei que um pipeline de comandos processa arquivos (texto, matrizes de strings) linha por linha. (Se cada comando funcionar, linha por linha.) Cada linha de texto passa pelo pipeline, os comandos não esperam que o anterior termine o processamento de toda a entrada.
Mas parece que não é assim.
Aqui está um exemplo de teste. Existem algumas linhas de texto. Eu as coloco em maiúsculas e repito cada linha duas vezes. Eu faço isso com cat text | tr '[:lower:]' '[:upper:]' | sed 'p'
.
Para seguir o processo, podemos executá-lo "interativamente" - pule o nome do arquivo de entrada cat
. Cada parte do pipeline é executada linha por linha:
$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2
Mas o pipeline completo espera que eu termine a entrada EOF
e só depois imprima o resultado:
$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D
Deveria ser assim? Por que não é linha por linha?
cat
buffer até o stdin fechar.tr
esed
fazer linhas de processo decat
antes fecha stdinRespostas:
Existe uma regra geral de armazenamento em buffer seguida pela biblioteca de E / S padrão C (
stdio
) usada pela maioria dos programas unix. Se a saída estiver indo para um terminal, ela será liberada no final de cada linha; caso contrário, ele será liberado apenas quando o buffer (8K no meu sistema Linux / amd64; pode ser diferente no seu) estiver cheio.Se todas as suas utilidades estavam seguindo a regra geral, você veria saída com atraso em todos os seus exemplos (
cat|sed
,cat|tr
, ecat|tr|sed
). Mas há uma exceção: o GNUcat
nunca armazena em buffer sua saída. Ele não usastdio
ou altera astdio
política de buffer padrão .Posso ter certeza de que você está usando o GNU
cat
e não algum outro unixcat
porque os outros não se comportariam dessa maneira. O unix tradicionalcat
tem uma-u
opção para solicitar saída sem buffer. O GNUcat
ignora a-u
opção porque sua saída é sempre sem buffer.Portanto, sempre que você tiver um canal com um
cat
à esquerda, no sistema GNU, a passagem de dados pelo canal não será atrasada. Ocat
não está mesmo indo linha por linha - o terminal está fazendo isso. Enquanto você digita a entrada para gato, seu terminal está no modo "canônico" - baseado em linha, com teclas de edição como backspace e ctrl-U, oferecendo a você a chance de editar a linha que você digitou antes de enviá-la Enter.No
cat|tr|sed
exemplo,tr
ainda está recebendo dadoscat
assim que você pressiona Enter, mastr
segue astdio
política padrão: sua saída está indo para um canal, para que não seja liberada após cada linha. Ele grava no segundo canal quando o buffer está cheio ou quando um EOF é recebido, o que ocorrer primeiro.sed
também segue astdio
política padrão, mas sua saída está indo para um terminal, para que ele escreva cada linha assim que terminar. Isso afeta o quanto você deve digitar antes que algo apareça na outra extremidade do pipeline - se osed
buffer estivesse bloqueando sua saída, você teria que digitar o dobro (para preenchertr
o buffer de saída esed
a saída) amortecedor).O GNU
sed
tem-u
opção, portanto, se você reverter a ordem e usá-cat|sed -u|tr
lo, verá a saída aparecer instantaneamente novamente. (Ased -u
opção pode estar disponível em outro lugar, mas eu não acho que seja uma tradição antiga do unixcat -u
). Até onde eu sei, não há uma opção equivalentetr
.Existe um utilitário chamado
stdbuf
que permite alterar o modo de buffer de qualquer comando que use osstdio
padrões. É um pouco frágil, pois é usadoLD_PRELOAD
para realizar algo que a biblioteca C não foi projetada para oferecer suporte, mas, neste caso, parece funcionar:fonte
tee
edd
também geralmente jogam de acordo com suas próprias regras. Quando combinadas de forma criativa, as três ferramentas podem negar de maneira portável qualquer necessidade destdbuf
pipelines em segundo plano.Isso realmente me levou a pensar um pouco para entender e ainda mais para responder. Ótima pergunta (votarei em seguida).
Você esqueceu de tentar
tr | sed
nos itens de depuração acima:Então, evidentemente,
tr
buffers. Aprenda algo novo todos os dias!EDIT :
Enquanto penso sobre isso, isolamos a causa, mas não fornecemos uma explicação. Se você
cat | tr
, ele escreve imediatamente, se vocêcat | sed
, ele escreve imediatamente, mas se vocêtr | sed
, ele espera paraEOF
. Gostaria de sugerir a resposta pode ser enterrado emtr
oused
código-fonte, em seguida, e não ser um problema de tubulação.EDIT :
Vejo Wumpus fornecer a explicação enquanto eu estava digitando a última edição. Obrigado!
fonte
stdbuf
que também pode ser útil. unix.stackexchange.com/questions/182537/…