Como faço um loop nas linhas em STDIN e executo um comando shell?

Como faço um loop nas linhas em STDIN e executo um comando shell?

Gostaria de executar um comando shell em cada linha retirada do STDIN.

Neste caso, eu gostaria de executar xargs mv. Por exemplo, dadas duas linhas:

mfoo foo
mbar bar

Eu gostaria de executar:

xargs mv mfoo foo
xargs mv mbar bar

Eu tentei as seguintes estratégias com ruby, awke xargs. No entanto, estou fazendo errado:

Apenas xargs:

$ echo "mbar bar\nmbaz baz" | xargs mv
usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory

Através awk:

$ echo "mbar bar\nmbaz baz" | awk '{ system("xargs $0") }'

Através ruby:

$ echo "mbar bar\nmbaz baz" | ruby -ne '`xargs mv`'
$ ls
cat  foo  mbar mbaz

Eu tenho algumas questões:

  • Como faço o que estou tentando fazer?
  • O que há de errado com cada uma das minhas tentativas?
  • Existe uma maneira melhor de "pensar" no que estou tentando fazer?

Estou especialmente confuso porque minha xargstentativa não está funcionando porque o seguinte funciona:

$ echo "foo\nbar" | xargs touch
$ ls
bar foo

Responder1

echo "mbar bar\nmbaz baz" | xargs mv

Com xargsvocê deve usar a -topção para ver o que está acontecendo. Então, no caso acima, se invocássemos xargswith -t, o que veríamos:

mv mbar bar mbaz baz

Então obviamente não está correto. O que aconteceu foi que, xargscomo um crocodilo faminto, comeu todos os argumentos que lhe foram alimentados através do cano echo. Então você precisa de uma maneira de limitar a liberação de argumentos ao crocodilo. E como você solicitou por linha, o que você precisa é da opção -lou -Lpara POSIX.

echo "mbar bar\nmbaz baz" | xargs -l -t mv

Maneira POSIX:

echo "mbar bar\nmbaz baz" | xargs -L 1 -t mv

mv mbar bar
mv mbaz baz

E é isso que você queria. HTH

Responder2

Você pode seguir este caminho:

echo -e "foo bar\ndoo dar" | xargs -n 2 mv

Para ler do arquivo:

cat lines.txt | xargs -n 2 mv

ou

xargs -a lines.txt -n 2 mv

Responder3

xargspode não se comportar como você espera. Veja o comentário de @MichaelHomer sobre esta questão:

xargs mv mfoo fooesperará pela entrada e executará mv mfoo foo $x_1 $x_2 $x_3...para cada linha $x_nde entrada obtida. @MichaelHomer

Isto pode ser confirmado lendo oxargspágina de manual:

DESCRIÇÃO
O utilitário xargs lê strings delimitadas por espaço, tabulação, nova linha e fim de arquivo da entrada padrão e executa o utilitário com as strings como argumentos.

Na prática, parece que xargsirá ignorar novas linhas ao alimentar argumentos para o utilitário de linha de comando fornecido como seu primeiro argumento. Por exemplo, observe o que acontece com a nova linha:

$ echo "foo bar\ncat dog"
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs echo
foo bar cat dog

Este comportamento pode ser controlado com o-nbandeira:

-n número

Defina o número máximo de argumentos obtidos da entrada padrão para
cada invocação de utilitário.

Voltando ao exemplo anterior se quiséssemosxargs pegar apenas 2 argumentos e sair, podemos usarAbordagem de @ddnomad:

$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog

Além disso, conforme mostrado nos comentários da resposta de Rakesh Sharma, no BSD xargsvocê pode especificar o número de linhas a serem lidas com o -Lsinalizador. Na página de manual:

-L número

Utilitário de chamada para cada linha numérica lida. Se o EOF for alcançado e
menos linhas forem lidas do que o número, o utilitário será chamado com as linhas disponíveis.

Igualmente útil para testes é a -topção:

Revisitando nosso exemplo, ambos os itens a seguir funcionarão; entretanto, o segundo exemplo é melhor porque é o que se pretendia neste caso:

$ echo "foo bar\ncat dog" | xargs -n 2 echo
foo bar
cat dog
$ echo "foo bar\ncat dog" | xargs -L 1 echo
foo bar
cat dog

Nota da página de manual:

As opções -L e -n são mutuamente exclusivas; o último fornecido será usado.

Responder4

Existe uma solução muito simples que não usa xargs.
Defina esta função:

$ mv2() { while (($# >1 )); do echo mv "$1" "$2"; shift 2; done; }

Em seguida, basta chamá-lo com a lista de nomes de arquivos necessários (sem necessidade de novas linhas):

$ mv2 mbar bar mbaz baz
mv mbar bar
mv mbaz baz

Remova o eco para fazer a função realmente funcionar.

informação relacionada