Por que alguns programas (como o readlink) não podem receber dados de um pipe?

7

Eu tenho um link simbólico para um script no meu $PATH cujo arquivo eu queria editar. Eu esqueci o filepath, então eu tentei fazer o seguinte:

$ which my_script_link | readlink

Eu esperava que a saída do caminho de arquivo, mas em vez disso, saída

> readlink: missing operand
> Try 'readlink --help' for more information

Eu já vi um comportamento semelhante antes em outras situações (como tentar enviar uma lista de arquivos para o vim para edição). Eu sei que existem soluções alternativas, como um subshell readlink $(which my_script_link) , mas eu quero entender porque o encanamento não funciona da maneira que eu acho que deveria nessa situação.

Obrigado!

    
por Nathan Wallace 02.10.2013 / 21:07

3 respostas

10

Simplificando, porque o programa não está escrito para isso. Cabe aos programadores decidir se o software pode ler a partir de STDIN ou se requer um arquivo de entrada. No caso de readlink , seus estados de página man (ênfase minha):

readlink - print resolved symbolic links or canonical file names

SYNOPSIS
readlink [OPTION]... FILE...

Como regra geral, os programas que recebem entrada de STDIN são projetados para, de alguma forma, analisar essa entrada. Quando você canaliza dados para readlink , ele recebe um fluxo de texto e não tem ideia do que fazer com ele, pois ele lida apenas com arquivos e não com o conteúdo deles. O mesmo vale para programas como ls ou cd ou cp etc. Somente programas que lidam com fluxos de texto / dados podem aceitar entrada de um canal.

    
por 02.10.2013 / 21:14
6

Se um programa recebe sua entrada de stdin ou como argumentos de linha de comando depende do designer. Ambas as abordagens têm seus méritos. Para programas que operam estritamente em arquivos, no entanto, geralmente é mais conveniente passar os nomes de arquivos como argumentos de linha de comando, em vez de usar stdin .

A razão mais óbvia é que o caso comum, o de simplesmente executar um programa em um arquivo, é mais fácil de digitar: readlink file em vez de echo file | readlink .

Um problema mais sutil é a correção. O problema é que quando os nomes de arquivos são passados através de stdin , o programa precisa ser capaz de distinguir um nome de arquivo de outro. Geralmente, isso é feito presumindo-se que os nomes dos arquivos sejam separados por espaço em branco ou por novas linhas, mas isso é incorreto, pois os nomes dos arquivos podem incluir espaços em branco. Uma maneira melhor é separar os nomes de arquivos com bytes nulos, mas pode ser inconveniente gerar uma lista de arquivos separados por nulos.

Passar nomes de arquivos na linha de comando evita esse problema porque o shell manipula toda a análise e cotação do programa. Você pode digitar touch $'foo\nbar' e touch corretamente como um nome de arquivo contendo uma nova linha, sem precisar manipular nenhuma análise especial ou citá-la.

Tudo isso dito, se você gostaria de passar arquivos através de stdin para um programa em particular, você pode. É para isso que o xargs é. xargs permite pegar um programa que aceita apenas argumentos na linha de comando e, em vez disso, fazer com que ele leve seus argumentos por stdin .

Em outras palavras, permite fazer isso: which my_script | xargs readlink .

Se você quisesse sempre fazer readlink funcionar dessa maneira, poderia criar um alias: alias readlink="xargs readlink" . Isso permitiria digitar which my_script | readlink , como você queria originalmente.

    
por 03.10.2013 / 09:16
3

Para comandos que não aceitam argumentos via STDIN, você pode usar este código pragma:

$ readlink $(which my_script_link)

Exemplo

$ ln -s /bin/ls ~/bin/somecmd

Agora verifique se está no $PATH .

$ which somecmd
~/bin/somecmd

Ou a maneira preferida, usando type instad de which :

$ type somecmd
somecmd is /home/saml/bin/somecmd

Ou apenas o valor:

$ type -P somecmd 
/home/saml/bin/somecmd

Agora corremos readlink :

$ readlink $(type -P somecmd)
/bin/ls
    
por 02.10.2013 / 22:03

Tags