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

15

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

$ command -r FILENAME

Suponha que FILENAME seja um arquivo contendo uma lista de nomes de arquivos, já que foi gerado usando ls > FILENAME .

Como posso, em vez disso, alimentar o comando com o resultado de ls diretamente? Na minha cabeça, algo assim deveria ser possível:

$ ls | command -r

Mas isso não acontece, a saída de ls não fica viciada como um argumento. Saída:

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

Como eu poderia obter o efeito desejado?

    
por Paolo 19.07.2011 / 20:52

5 respostas

26

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

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

    ls | command -r -
    
  • Muitas variantes unix fornecem arquivos especiais em /dev que designam os descritores padrão. Se /dev/stdin existir, abri-lo e ler a partir dele é equivalente a ler da entrada padrão; da mesma forma /dev/fd/0 se existir.

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

    command -r <(ls)
    
  • Se o comando espera que o nome tenha um formulário específico (normalmente 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 ls tende a mangle nomes de arquivos quando eles contêm caracteres não imprimíveis. printf '%s\n' * é mais confiável - imprimirá cada byte literalmente em nomes de arquivos. Nomes de arquivos contendo novas linhas ainda causarão problemas, mas isso é inevitável se o comando esperar uma lista de nomes de arquivos separados por novas linhas.

    
por 19.07.2011 / 22:14
6

Deve ser:

ls | xargs -n 1 command -r

Editar: para nomes com espaços em branco:

ls | xargs -d '\n' -n 1 command -r
    
por 19.07.2011 / 21:40
1

Isso deve funcionar para o seu objetivo: ls | command -r /dev/stdin

    
por 19.07.2011 / 21:52
1

Na verdade, a única solução confiável que conheço que pode manipular 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, neste caso, é o caractere nulo, que não é permitido em nomes de arquivos, de qualquer maneira.

    
por 17.01.2013 / 13:25
0

Muitos comandos aceitam - como um "nome de arquivo" que significa "use a entrada padrão", mas esta convenção está longe de ser universal. Leia a man page.

    
por 19.07.2011 / 21:05

Tags