Usando dados lidos de um canal em vez de um arquivo nas opções de comando

18

Por definição, este comando obtém a entrada de um arquivo.

$ command -r FILENAME

Suponha que FILENAMEseja um arquivo que contém uma lista de nomes de arquivos, conforme foi gerado usando ls > FILENAME.

Como posso alimentar o comando lsdiretamente com o resultado ? Na minha cabeça, algo como isto deveria ser possível:

$ ls | command -r

Mas isso não acontece, a saída de lsnão fica viciada como argumento. Resultado:

Usage: command -r FILENAME
error: -r option requires an argument

Como eu poderia obter o efeito desejado?

Paolo
fonte
Mais respostas sobre isso em uma pergunta relacionada: Como passar uma string para um comando que espera um arquivo?
ilkkachu 13/03

Respostas:

31

Isso depende do comando. Alguns comandos que lêem de um arquivo esperam que o arquivo seja um arquivo regular, cujo tamanho seja conhecido antecipadamente e que possa ser lido em qualquer posição e rebobinado. Isso é improvável se o conteúdo do arquivo for uma lista de nomes de arquivos: o comando provavelmente será o conteúdo de um canal que será lido apenas sequencialmente do início ao fim. Existem várias maneiras de alimentar dados através de um canal para um comando que espera um nome de arquivo.

  • Muitos comandos tratam -como um nome especial, que significa ler da entrada padrão em vez de abrir um arquivo. Esta é uma convenção, não uma obrigação.

    ls | command -r -
  • Muitas variantes do unix fornecem arquivos especiais /devque designam os descritores padrão. Se /dev/stdinexistir, abri-lo e lê-lo é equivalente a ler da entrada padrão; da mesma forma, /dev/fd/0se existir.

    ls | command -r /dev/stdin
    ls | command -r /dev/fd/0
  • Se o seu shell for ksh, bash ou zsh, você poderá fazer com que o shell lide com o negócio de alocar algum descritor de arquivo. A principal vantagem desse método é que ele não está vinculado à entrada padrão; portanto, você pode usar a entrada padrão para outra coisa e usá-la mais de uma vez.

    command -r <(ls)
  • Se o comando espera que o nome tenha uma forma específica (geralmente uma extensão específica), você pode tentar enganá-lo com um link simbólico.

    ln -s /dev/fd/0 list.foo
    ls | command -r list.foo

    Ou você pode usar um pipe nomeado.

    mkfifo list.foo
    ls >list.foo &
    command -r list.foo

Observe que gerar uma lista de arquivos com lsé problemático porque lstende a alterar os nomes dos arquivos quando eles contêm caracteres não imprimíveis. printf '%s\n' *é mais confiável - ele imprime todos os bytes literalmente nos nomes dos arquivos. Os nomes de arquivos que contêm novas linhas ainda causarão problemas, mas isso é inevitável se o comando esperar uma lista de nomes de arquivos separados por novas linhas.

Gilles 'SO- parar de ser mau'
fonte
+1 para enumerar todas as possibilidades. A substituição do processo da IMO é a resposta correta para essa situação.
21119 Glenn Jackman
7

Deveria ser:

ls | xargs -n 1 command -r

Editar: para nomes com espaços em branco:

ls | xargs -d '\n' -n 1 command -r
ghm1014
fonte
Isso pode ser problemático se commandacreditar que possui todos os nomes de arquivos necessários na linha de comando de uma só vez. Algumas versões do xargs fornecerão commandalguns nomes de arquivos (10, ao que me parece) por vez. Parece que o GNU xargs oferece tudo de uma vez.
22611 Bruce Ediger
2

Na verdade, a única solução confiável que eu conheço que pode lidar com todos os nomes de arquivos, incluindo aqueles com uma nova linha, é:

find . -maxdepth 1 -print0 | xargs -n 1 -0 command -r

O único caractere não permitido no nome do arquivo nesse caso é o caractere nulo, que não é permitido nos nomes dos arquivos.

Linus Swälas
fonte
1

Muitos comandos aceitam -como "nome do arquivo" que significa "use a entrada padrão", mas essa convenção está longe de ser universal. Leia a página do manual.

dmckee
fonte
1

Isso deve funcionar para o seu propósito: ls | command -r /dev/stdin

alex
fonte