Eu tenho um link simbólico para um script cujo $PATH
arquivo 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 man
pá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 readlink
ele, 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 ls
or cd
ou cp
etc. Somente programas que lidam com fluxos de texto/dados podem aceitar entrada de um canal.
Responder2
Se um programa obtém sua entrada stdin
ou 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 file
em 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 touch
ver 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 stdin
para um programa específico, você pode. É para isso que xargs
serve. xargs
permite 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 readlink
o trabalho dessa forma, você poderia criar um alias: alias readlink="xargs readlink"
. Isso permitiria que você digitasse which my_script | readlink
como 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 type
instad 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