Como eu faço um loop sobre as linhas em STDIN e executo um comando shell?

0

Gostaria de executar um comando shell em cada linha tirada de STDIN.

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

mfoo foo
mbar bar

Gostaria de executar:

xargs mv mfoo foo
xargs mv mbar bar

Eu tentei as estratégias a seguir 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 de awk :

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

Através de ruby :

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

Eu tenho algumas perguntas:

  • 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" sobre o que estou tentando fazer?

Estou especialmente confuso com o fato de minha tentativa de xargs não funcionar porque os itens a seguir funcionam:

$ echo "foo\nbar" | xargs touch
$ ls
bar foo
    
por mbigras 10.04.2017 / 00:01

4 respostas

2
echo "mbar bar\nmbaz baz" | xargs mv

Com xargs , você deve usar a opção -t para ver o que está acontecendo. Então, no seu caso acima, se invocássemos xargs com -t , o que vemos:

mv mbar bar mbaz baz

Então, obviamente, não está correto. O que aconteceu foi que xargs gosta de um crocodilo faminto, comeu todos os args alimentados através do tubo por echo . Então você precisa de uma maneira de limitar a liberação de argumentos para o croc. 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

via POSIX-ly:

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

mv mbar bar
mv mbaz baz

E é isso que você queria. H.T.H

    
por 10.04.2017 / 01:42
2

Você pode seguir este caminho:

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

Para ler o arquivo:

cat lines.txt | xargs -n 2 mv

ou

xargs -a lines.txt -n 2 mv
    
por 10.04.2017 / 00:15
0

xargs pode não se comportar como você espera. Veja o comentário de @ MichaelHomer a esta pergunta:

xargs mv mfoo foo will wait for input and run mv mfoo foo $x_1 $x_2 $x_3... for every line $x_n of input it gets. @MichaelHomer

Isso pode ser confirmado lendo o xargs página do manual :

DESCRIPTION
The xargs utility reads space, tab, newline and end-of-file delimited strings from the standard input and executes utility with the strings as arguments.

Na prática, parece que xargs irá ignorar novas linhas quando alimentando argumentos para o utilitário de linha de comando dado 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

Esse comportamento pode ser controlado com o -n sinalizador :

-n number

Set the maximum number of arguments taken from standard input for
each invocation of utility.

Voltando ao exemplo anterior, se quiséssemos que xargs aceitasse apenas 2 args, então parássemos então podemos usar a abordagem @ ddnomad :

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

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

-L number

Call utility for every number lines read. If EOF is reached and
fewer lines have been read than number then utility will be called with the available lines.

Igualmente útil para o teste é a alteração -t :

Revisitando nosso exemplo, os dois seguintes funcionarão; no entanto, o segundo exemplo é melhor porque é o que 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 na página do manual:

The -L and -n options are mutually exclusive; the last one given will be used.

    
por 10.04.2017 / 01:49
0

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 que a função realmente funcione.

    
por 10.04.2017 / 05:06