Por que certos programas (como o readlink) não podem receber informações de um canal?

Por que certos programas (como o readlink) não podem receber informações de um canal?

Eu tenho um link simbólico para um script cujo $PATHarquivo eu queria editar. Esqueci o caminho do arquivo, então tentei fazer o seguinte:

$ which my_script_link | readlink

Eu esperava que isso gerasse o caminho do arquivo, mas em vez disso ele gerou

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

Já vi um comportamento semelhante 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 quero entenderpor quea tubulação não funciona da maneira que acho que deveria nesta situação.

Obrigado!

Responder1

Simplificando, porque o programa não foi escrito para isso. Cabe aos programadores decidir se seu software pode ler STDIN ou se requer um arquivo de entrada. No caso de readlink, sua manpágina afirma (ênfase minha):

readlink - impressão resolvidalinks simbólicosou canôniconomes de arquivos

link de leitura da SINOPSE
[OPÇÃO]...ARQUIVO...

Como regra geral, os programas que recebem informações do STDIN são projetados para analisar de alguma forma essa entrada. Quando você canaliza dados para readlinkele, ele recebe um fluxo de texto e não tem ideia do que fazer com ele, pois lida apenas com arquivos e não com seu conteúdo. O mesmo se aplica a programas como lsor cdou cpetc. Somente programas que lidam com fluxos de texto/dados podem aceitar entrada de um canal.

Responder2

Se um programa obtém sua entrada stdinou como argumentos de linha de comando, depende do designer. Ambas as abordagens têm seus méritos. No entanto, para programas que operam estritamente em arquivos, geralmente é mais conveniente passar os nomes dos arquivos como argumentos de linha de comando, em vez de por meio de stdin.

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

Uma questão mais sutil é a correção. O problema é que quando nomes de arquivos são transmitidos stdin, o programa precisa ser capaz de distinguir um nome de arquivo de outro. Freqüentemente, isso é feito assumindo que os nomes dos arquivos são separados por espaços em branco ou novas linhas, mas isso é incorreto, pois os nomes dos arquivos podem incluir espaços em branco. A melhor maneira é separar os nomes dos 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 cuida de toda a análise e citação do programa. Você pode digitar touch $'foo\nbar'e touchver isso corretamente como um nome de arquivo contendo uma nova linha, sem precisar lidar com nenhuma análise especial ou citação.

Dito isso, se quiser passar arquivos stdinpara um programa específico, você pode. É para isso que xargsserve. xargspermite que você pegue um programa que aceita apenas argumentos na linha de comando e, em vez disso, faça com que ele transmita seus argumentos stdin.

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

Se você quisesse sempre fazer readlinko trabalho dessa forma, você poderia criar um alias: alias readlink="xargs readlink". Isso permitiria que você digitasse which my_script | readlinkcomo você queria originalmente.

Responder3

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

$ readlink $(which my_script_link)

Exemplo

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

Agora verifique se está no $PATH.

$ which somecmd
~/bin/somecmd

Ou a forma preferida, usando typeinstad of 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

informação relacionada