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
, awk
e 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 xargs
tentativa 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 xargs
você deve usar a -t
opção para ver o que está acontecendo. Então, no caso acima, se invocássemos xargs
with -t
, o que veríamos:
mv mbar bar mbaz baz
Então obviamente não está correto. O que aconteceu foi que, xargs
como 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 -l
ou -L
para 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
xargs
pode não se comportar como você espera. Veja o comentário de @MichaelHomer sobre esta questão:
xargs mv mfoo foo
esperará pela entrada e executará mvmfoo foo $x_1 $x_2 $x_3...
para cada linha$x_n
de entrada obtida. @MichaelHomer
Isto pode ser confirmado lendo oxargs
pá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 xargs
irá 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-n
bandeira:
-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 xargs
você pode especificar o número de linhas a serem lidas com o -L
sinalizador. 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 -t
opçã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.