Por que a substituição do processo BASH não funciona com alguns comandos?

29

Ocasionalmente, a substituição do processo não funcionará conforme o esperado. Aqui está um exemplo:

Entrada:

gcc <(echo 'int main(){return 0;}')

Saída:

/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status

Entrada:

Mas funciona como esperado quando usado com um comando diferente:

grep main <(echo 'int main(){return 0;}')

Saída:

int main(){return 0;}

Eu notei falhas semelhantes com outros comandos (ou seja, o comando que espera o arquivo da substituição do processo não pode usar /dev/fd/63ou semelhante). Essa falha gccé apenas a mais recente. Existe alguma regra geral que eu deva conhecer para determinar quando a substituição do processo falhará dessa maneira e não deve ser usada?

Estou usando esta versão BASH no Ubuntu 12.04 (também vi isso no arch e no debian):
GNU bash, versão 4.3.11 (1) -release (i686-pc-linux-gnu)

Lotney
fonte
1
illegal seekparece a resposta - a |pipeque bashaponta o programa executado não é um arquivo procurável. Provavelmente, se você não conseguir obter êxito echo data | command /dev/fd/0em um programa, terá sorte semelhante com <(cmd). Ele não fornece um arquivo em disco - apenas substitui um argumento que aponta para um descritor de arquivo pipe.
mikeserv
2
Nesse caso específico, embora o gcc possa aceitar entrada padrão, ele (por padrão) usa a extensão do nome do arquivo para determinar o idioma. Então tente gcc -xc <(echo 'int main(){return 0;}')(que define o idioma Cexplicitamente).
steeldriver
Fui orientado aqui em resposta à minha própria pergunta, que parece provável ser outro exemplo disso. superuser.com/questions/1243405 . Obrigado por formular a pergunta melhor do que eu era capaz.
Jonathan Hartley

Respostas:

33

A substituição do processo resulta em um arquivo especial (como /dev/fd/63no seu exemplo) que se comporta como o final de leitura de um pipe nomeado. Este arquivo pode ser aberto e lido, mas não gravado, não procurado.

Comandos que tratam seus argumentos como fluxos puros funcionam, enquanto comandos que esperam buscar nos arquivos que recebem (ou gravá-los) não funcionam. O tipo de comando que vai funcionar é o que é geralmente considerado um filtro: cat, grep, sed, gzip, awk, etc ... Um exemplo de um comando que não vai funcionar é um editor como viou uma operação de arquivo como mv.

gccdeseja poder executar acesso aleatório em seus arquivos de entrada para detectar em qual idioma eles estão escritos. Se você der gccuma dica sobre o idioma do arquivo de entrada, será um prazer transmitir o arquivo:

gcc -x c <(echo 'int main(){return 0;}')

A forma mais simples e direta, sem substituição de processo, também funciona:

echo 'int main(){return 0;}' | gcc -x c -

Observe que isso não é específico para bash. Todos os shells que suportam a substituição de processos se comportam da mesma maneira.

Celada
fonte
+1 para a solução alternativa do gcc, mas não tenho certeza do seu ponto em relação aos arquivos. O <()formato deve funcionar como um arquivo para todos os efeitos. Na verdade, não conheço nenhum comando que espere um arquivo que não seja feliz <(). Os que não funcionam são aqueles que esperam nomes de arquivos , não arquivos. Por exemplo, grep -fespera um arquivo e funciona bem <().
terdon
4
@terndon Com certeza <()produz um nome de arquivo (a construção se expande /proc/self/fd/somethingno meu sistema). Esse nome, quando aberto, atua como o final de leitura de um pipe nomeado ( S_IFIFO), em vez de um arquivo comum ( S_IFREG), em suportes read()e outros, mas não seek().
Celada 25/10
7
Note-se que zshsuporta uma terceira forma de substituição processo que utiliza arquivos temporários, especialmente para o efeito:gcc =(echo 'int main(){return 0;}')
Stéphane Chazelas
talvez relacionado , mas funciona com, <(echo '...')mas não com <(git show ...). Alguma idéia do porquê disso?
Jörn Hees
2
O GCC "não realiza acesso aleatório em seus arquivos de entrada para detectar em que idioma eles estão gravados". Apenas analisa a extensão do nome do arquivo. Se o nome do arquivo não tiver uma extensão (ou se houver uma que não seja reconhecida), o GCC assume que o arquivo é um arquivo de objeto ou script vinculador e o passa para ld(que detecta os formatos dos objetos). -xnão é uma dica; é uma declaração. Se você especificar -x f95, o GCC passará o arquivo para o compilador Fortran-95, independentemente de seu nome ou conteúdo. Veja gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Overall-Options.html
rici