Como eu executo um comando em cada entrada dada uma lista de entrada

2

Eu tenho um monte de pastas, e eu quero cd em cada pasta e executar um comando. Eu sinto que deveria ser algo assim:

(O seguinte é um exemplo simplificado e simplificado do que eu quero fazer).

ls | xargs echo 'Looking at {}' && cd {} && /bin/do_stuff

Mas isso não funciona. Como faço isso?

    
por user68331 23.05.2014 / 02:45

2 respostas

2

Não funciona porque xargs executa seu comando diretamente. Não aceita uma linha de código de shell e passa o comando ao shell para executar. De fato, todos os && s estão sendo interpretados pelo shell antes de ls e xargs serem executados. Além disso, {} é uma construção que find entende, não xargs .

(Você pode usar -I com xargs para imitar esse recurso de find , se você quiser.)

A maneira mais simples de fazer isso com xargs é colocar seu comando em um script de shell:

#!/bin/sh
echo "Looking at $1"
cd $1
/bin/do_stuff

(Eu substituí efetivamente seu && por ; , pois parece que echo e cd não podem falhar, contanto que você execute isso em um diretório que contenha apenas outros subdiretórios. Se você precisar " e "comportamento, a correção necessária para o script é óbvia.)

Em seguida, execute esse script de shell por meio de xargs . Se você chamou de dostuff.sh :

$ ls | xargs ./dostuff.sh

dostuff.sh precisa ser marcado como executável, como acontece com chmod +x dostuff.sh .

Provavelmente, existe uma maneira de escrever tudo isso em uma única linha com sh -c , mas se você não se lembra da mágica para fazer isso, é apenas uma curiosidade, não é?

    
por 23.05.2014 / 03:02
0

xargs é a ferramenta certa, mas é difícil de usar.

xargs não executa um shell, a menos que você o diga. Ele executa o comando fornecido no primeiro argumento não opcional e passa os outros argumentos em sua linha de comandos, seguidos pelos argumentos lidos da entrada padrão. Por exemplo

echo hello world | xargs echo 'Looking at'

executa echo 'Looking at' hello world . Para executar o comando uma vez por linha de entrada, use xargs -L1 :

(echo hello; echo world) | xargs -L1 echo 'Looking at'

Como você quer que xargs execute um comando shell, é necessário invocar um shell explicitamente.

(echo hello; echo world) | xargs sh -c 'echo "Looking at $0" && cd "$0" && /bin/do_stuff'

(Ou tcsh -c se você realmente quiser, mas venha um, este é o século 21.)

Você pode usar -I {} para interpolar {} dentro do comando, mas cuidado, pois isso só funciona se a entrada não contiver nenhum caractere especial de shell. Passar a entrada como um argumento para o comando ( $0 acima) tem a vantagem de não fazer nenhuma suposição sobre a entrada (além de ser separada por linha e não conter espaços em branco finais, conforme xargs -L sintaxe).

Em um shell no estilo Bourne / POSIX, uma abordagem alternativa que usa mais linhas, mas é muito mais clara, é usar um loop em torno do read shell embutido:

{ echo hello; echo world; } |
while IFS= read -r line; do
  echo "Looking at $line"
  (cd "$line" && /bin/do_stuff)
done

(Note que eu executo cd em um subshell, porque todo o loop é executado no mesmo shell e eu não quero alterar o diretório de trabalho do shell).

    
por 24.05.2014 / 03:45