Eu tenho um binário (que não posso modificar) e posso fazer:
./binary < file
Eu também posso fazer:
./binary << EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF
Mas
cat file | ./binary
me dá um erro. Não sei por que não funciona com um cano. Nos três casos, o conteúdo do arquivo é dado à entrada padrão do binário (de maneiras diferentes):
- bash lê o arquivo e o entrega ao stdin de binário
- bash lê linhas de stdin (até EOF) e dá a stdin de binário
- gato lê e coloca as linhas de arquivo em stdout, o bash as redireciona para stdin of binary
O binário não deve notar a diferença entre os três, tanto quanto eu entendi. Alguém pode explicar por que o terceiro caso não funciona?
BTW: O erro dado pelo binário é:
20170116 / 125624.689 - U3000011 Não foi possível ler o arquivo de script '', código de erro '14'.
Mas minha pergunta principal é: como existe uma diferença para qualquer programa com essas três opções.
Aqui estão alguns detalhes adicionais: tentei novamente com strace e, de fato, ocorreram alguns erros ESPIPE (busca ilegal) de lseek, seguidos por EFAULT (endereço incorreto) da leitura logo antes da mensagem de erro.
O binário que tentei controlar com um script ruby (sem usar arquivos temporários) faz parte do callapi da Automic (UC4) .
fonte
isatty()
retorna false para será um arquivo pesquisável ou mmappable ...cat
. Parece que você não pode usá-lo para combinar dois arquivos, como é o uso pretendido.Respostas:
No
binary
stdin é o arquivo aberto no modo somente leitura. Observe quebash
ele não lê o arquivo, apenas o abre para leitura no descritor de arquivo 0 (stdin) do processo em que ele é executadobinary
.No:
Dependendo do shell,
binary
o stdin será um arquivo temporário excluído (AT&T ksh, zsh, bash ...) que contenhatest\n
como colocado ali pelo shell ou pela extremidade de leitura de um pipe (dash
,yash
; e o shell gravatest\n
em paralelo na outra extremidade do tubo). No seu caso, se você estiver usandobash
, seria um arquivo temporário.No:
Dependendo do shell,
binary
o stdin será a extremidade de leitura de um tubo ou a extremidade de um par de soquetes em que a direção de gravação foi desativada (ksh93) ecat
está gravando o conteúdo dafile
outra extremidade.Quando stdin é um arquivo regular (temporário ou não), ele pode ser procurado.
binary
pode ir para o início ou o fim, retroceder, etc. Também pode mapeá-lo, fazer algunsioctl()s
como FIEMAP / FIBMAP (se usar em<>
vez de<
, poderá truncar / fazer furos nele, etc.).pares de tubos e soquetes, por outro lado, são um meio de comunicação entre processos, não há muito o
binary
que fazer alémread
dos dados (embora também existam algumas operações, como algunsioctl()
s específicos de um tubo, que eles poderiam fazer neles e não em arquivos regulares) .Na maioria das vezes, é a capacidade que faltava para
seek
que faz com que aplicativos para falhar / reclamar quando se trabalha com tubos, mas poderia ser qualquer uma das outras chamadas de sistema que são válidas em arquivos regulares, mas não em diferentes tipos de arquivos (comommap()
,ftruncate()
,fallocate()
) . No Linux, também há uma grande diferença de comportamento quando você abre/dev/stdin
enquanto o fd 0 está em um pipe ou em um arquivo regular.Existem muitos comandos por aí que só podem lidar com arquivos pesquisáveis , mas quando esse é o caso, geralmente não é para os arquivos abertos em seu stdin.
unzip
precisa ler o índice armazenado no final do arquivo e, em seguida, procurar dentro do arquivo para ler os membros do arquivo. Mas aqui, o arquivo (regular no primeiro caso, canal no segundo) é fornecido como argumento de caminhounzip
e ounzip
abre por si próprio (normalmente em fd diferente de 0) em vez de herdar um fd já aberto pelo pai. Ele não lê arquivos zip de seu stdin. stdin é usado principalmente para interação do usuário.Se você executar o
binary
seu sem redirecionamento no prompt de um shell interativo em execução em um emulador de terminal,binary
o stdin será herdado de seu pai, o shell, que por sua vez o herdará de seu pai, o emulador de terminal e será um dispositivo pty aberto no modo de leitura + gravação (algo como/dev/pts/n
).Esses dispositivos também não são procuráveis. Portanto, se
binary
funcionar bem ao receber informações do terminal, possivelmente o problema não é procurar.Se esse número 14 for um erro (um código de erro definido por falhas nas chamadas do sistema), na maioria dos sistemas, isso seria
EFAULT
( Endereço incorreto ). Aread()
chamada do sistema falharia com esse erro se solicitada a leitura em um endereço de memória que não seja gravável. Isso seria independente de o fd ler os dados dos pontos em um canal ou arquivo regular e geralmente indicaria um erro 1 .binary
possivelmente determina o tipo de arquivo aberto em seu stdin (withfstat()
) e se depara com um erro quando não é um arquivo comum nem um dispositivo tty.Difícil dizer sem saber mais sobre o aplicativo. Executá-lo sob
strace
(outruss
/tusc
equivalente em seu sistema) pode ajudar-nos a ver o que é a chamada de sistema se houver que está a falhar aqui.1 O cenário previsto por Matthew Ife em um comentário à sua pergunta parece muito plausível aqui. Citando-o:
fonte
./binary < file
é possível procurar informações redirecionadas no estilo de !open
editado e se comporta da mesma forma que qualquer arquivoopen
editado. Acontece que ele foi herdado de um processo pai, mas isso não é tão incomum.open("/proc/self/fd/0", O_RDWR)
funciona, mesmo em arquivos excluídos. Parvo eu: P.echo foo>foo; (sleep 0.5; ll -L /proc/self/fd/0; strace ./a.out; ll -L /proc/self/fd/0) < foo & sleep 0.1 && rm foo
unlinksfoo
antes do a.out ser executado com o stdin redirecionado defoo
.Aqui está um exemplo de programa simples que ilustra a resposta de Stéphane Chazelas usando
lseek(2)
sua entrada:Testando:
Tubos não são procuráveis, e esse é um lugar em que um programa pode reclamar de tubos.
fonte
O pipe e o redirecionamento são animais diferentes, por assim dizer. Quando você usa o
here-doc
redirecionamento (<<
) ou o stdin,<
o texto não sai do nada - ele realmente entra em um descritor de arquivo (ou arquivo temporário, se você preferir), e é aí que o stdin do binário estará apontando.Especificamente, aqui está um trecho do
bash's
código-fonte, arquivo redir.c (versão 4.3):Portanto, como o redirecionamento pode ser basicamente tratado como arquivos, os binários podem navegá-los ou
seek()
através do arquivo facilmente, saltando para qualquer byte do arquivo.Pipes, já que são buffers de 64 KiB (pelo menos no Linux) com gravações de 4096 bytes ou menos garantidos como atômicos, não são possíveis, ou seja, você não pode navegar livremente por eles - apenas lê sequencialmente. Uma vez eu implementei o
tail
comando em python. 29 milhões de linhas de texto podem ser pesquisadas em microssegundos se forem redirecionadas, mas se foremcat
enviadas por canal, não há nada que possa ser feito - portanto, tudo deve ser lido em sequência.Outra possibilidade é que o binário deseje abrir um arquivo especificamente e não queira receber entrada de um canal. Geralmente, isso é feito por meio de uma
fstat()
chamada do sistema e verificando se a entrada é proveniente de umS_ISFIFO
tipo de arquivo (que significa um canal / canal nomeado).Seu binário específico, como não sabemos o que é, provavelmente tenta procurar, mas não pode procurar tubos. É recomendável que você consulte sua documentação para descobrir o que exatamente significa o código de erro 14.
NOTA : Alguns shells, como o dash (Debian Almquist Shell, padrão
/bin/sh
no Ubuntu) implementamhere-doc
redirecionamento com pipes internamente , portanto, podem não ser procuráveis. O ponto permanece o mesmo - os pipes são seqüenciais e não podem ser navegados facilmente, e as tentativas resultarão em erros.fonte
dash
fazem. Essa resposta explica o comportamento observado com o bash, mas esse comportamento aparentemente não é garantido por outras conchas.dash
no meu sistema. Eu não estava ciente disso anteriormente. Obrigado por apontarfstat()
stdin para verificar se é um cachimbo.stat
leva um nome de caminho. Mas, na verdade, apenas tentarlseek
é provavelmente a maneira mais sensata de determinar se um fd é procurável depois de já estar aberto.A principal diferença está no tratamento de erros.
No caso a seguir, o erro é relatado
No caso a seguir, o erro não é relatado.
Com o bash, você ainda pode usar o PIPESTATUS:
Mas está disponível apenas imediatamente após a execução do comando:
Há outra diferença, quando usamos funções shell em vez de binários. Em
bash
, as funções que fazem parte de um pipeline são executadas em subcascas (exceto o último componente do pipeline, se alastpipe
opção estiver ativada ebash
não for interativa), portanto, a alteração de variáveis não terá efeitos no shell pai:fonte
>
é feito pelo shell, mas com o pipe é feito pelo comando que produz texto. ESTÁ BEM. Mas nesta questão específica, o OP está usando um arquivo existente, portanto esse não é o problema, e claramente o erro produzido é pelo binário.