Como aplicar o comando shell a cada linha de uma saída de comando?


192

Suponha que eu tenha alguma saída de um comando (como ls -1):

a
b
c
d
e
...

Eu quero aplicar um comando (digamos echo) a cada um, por sua vez. Por exemplo

echo a
echo b
echo c
echo d
echo e
...

Qual é a maneira mais fácil de fazer isso no bash?


3
ls -1pode ser um exemplo aqui, mas é importante lembrar que não é bom analisar a saída de ls. Veja: mywiki.wooledge.org/ParsingLs
codeforester

Respostas:


217

Provavelmente é mais fácil de usar xargs. No seu caso:

ls -1 | xargs -L1 echo

O -Lsinalizador garante que a entrada seja lida corretamente. Na página do manual de xargs:

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]

24
lsfaz automaticamente -1em um tubo.
Pausado até novo aviso.

5
@ Dennis, não parece com isso: ls | xargs -L2 echoe ls -1 | xargs -L2 echodê duas saídas diferentes. O primeiro sendo todos em uma linha.
Alex Budovski

2
@ Alex: Eu recebo a mesma saída.
Pausado até novo aviso.

6
xargspode executar apenas arquivos executáveis, não funções de shell ou comandos internos do shell. Para o primeiro, a melhor solução é provavelmente a que apresenta readum loop.
Pabouk

12
Desejo que esta resposta inclua uma explicação do que -L1é para isso.
219 Wyck

162

Você pode usar uma operação de pré-adição básica em cada linha:

ls -1 | while read line ; do echo $line ; done

Ou você pode canalizar a saída para sed para operações mais complexas:

ls -1 | sed 's/^\(.*\)$/echo \1/'

1
O comando sed parece não funcionar: sh: cho: not found a sh: cho: not foundparece que está aceitando o eeco como um comando sed ou algo assim.
Alex Budovski

+1 para o whileloop. cmd1 | while read line; do cmd2 $line; done. Ou while read line; do cmd2 $line; done < <(cmd1)que não cria um subshell. Esta é a versão simplificada do seu sedcomando:sed 's/.*/echo &/'
Pausado até novo aviso.

1
@ Alex: altere as aspas duplas para aspas simples.
Pausado até novo aviso.

5
Cite o "$line"loop while, para evitar a divisão de palavras.
Ignis

3
Tente usar read -r linepara evitar readmexer com caracteres de escape. Por exemplo , echo '"a \"nested\" quote"' | while read line; do echo "$line"; done"a "nested" quote", que perdeu sua fuga. Se echo '"a \"nested\" quote"' | while read -r line; do echo "$line"; doneconseguirmos, "a \"nested\" quote"como esperado. Veja wiki.bash-hackers.org/commands/builtin/read
Warbo

9

Você pode usar um loop for :

para arquivo em *; Faz
   eco "$ file"
feito

Observe que, se o comando em questão aceitar vários argumentos, o uso de xargs é quase sempre mais eficiente, pois ele só precisa gerar o utilitário em questão uma vez em vez de várias vezes.


1
Vale a pena descrever o uso adequado / seguro de xargs, ie. printf '%s\0' * | xargs -0 ...- caso contrário, é muito inseguro com nomes de arquivos com espaços em branco, citações, etc.
Charles Duffy

8

Você realmente pode usar o sed para fazer isso, desde que seja o GNU sed.

... | sed 's/match/command \0/e'

Como funciona:

  1. Substituir correspondência por correspondência de comando
  2. Na substituição, execute o comando
  3. Substitua a linha substituída pela saída do comando.

Fantástico. Esqueci de colocar o comando e no final, mas o fiz depois de ver sua resposta e funcionou. Eu estava tentando acrescentar um ID aleatório entre 1000 e 15000, quando o SED corresponde a uma linha. cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
SGSI


2

xargs falha com barras invertidas, aspas. Precisa ser algo como

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

xargs -0 opção:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1finaliza os itens com caracteres de nova linha, trconvertendo-os em caracteres nulos.

Essa abordagem é cerca de 50 vezes mais lenta do que a iteração manualmente com for ...(consulte a resposta de Michael Aaron Safyan ) (3,55s vs. 0,066s). Mas, para outros comandos de entrada, como localizar, localizar, ler de um arquivo ( tr \\n \\0 <file) ou similar, você precisa trabalhar xargsassim.


1

Melhor resultado para mim:

ls -1 | xargs -L1 -d "\n" CMD

Melhor, mas não perfeito. find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandtratará dos casos em que a saída de ls -1é ambígua; use -printf '%P\0'ao invés de -print0se você não quer uma liderança ./em cada um.
Charles Duffy

1

eu gosto de usar o gawk para executar vários comandos em uma lista, por exemplo

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

no entanto, a fuga dos personagens escapáveis ​​pode ficar um pouco peluda.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.