Como posso encontrar arquivos e usar xargs para movê-los?

25

Eu quero encontrar alguns arquivos e movê-los.

Eu posso encontrar o arquivo com:

$ find /tmp/ -ctime -1 -name x*

Eu tentei movê-los para o meu diretório ~/play com:

$ find /tmp/ -ctime -1 -name x* | xargs mv ~/play/

mas isso não funcionou. Obviamente, mv precisa de dois argumentos.
Não tenho certeza se (ou como) referenciar o item atual dos xargs 'no comando mv?

    
por Michael Durrant 16.09.2013 / 16:12

2 respostas

37

Veja a resposta de Stephane para o melhor método, dê uma olhada na minha resposta por razões de não usar as soluções mais óbvias (e razões pelas quais elas não são as mais eficientes).

Você pode usar a opção -I de xargs :

find /tmp/ -ctime -1 -name "x*" | xargs -I '{}' mv '{}' ~/play/

Que funciona em um mecanismo semelhante a find e {} . Eu também gostaria de citar o seu argumento -name (porque um arquivo começando com x no diretório atual seria globado no arquivo e passado como um argumento para ser encontrado - o que não dará o comportamento esperado!).

No entanto, conforme apontado por manatwork, conforme detalhado na página xargs man:

   -I replace-str
          Replace occurrences of replace-str in the initial-arguments with
          names read from standard input.  Also, unquoted  blanks  do  not
          terminate  input  items;  instead  the  separator is the newline
          character.  Implies -x and -L 1.

O importante a observar é que -L 1 significa que somente uma linha de saída de find será processada por vez. Isso significa que é sintaticamente o mesmo que:

find /tmp/ -ctime -1 -name "x*" -exec mv '{}' ~/play/

(que executa uma única operação mv para o arquivo each ).

Mesmo usando o argumento GNU -0 xargs e o argumento find -print0 causa exatamente o mesmo comportamento de -I - isso é para clone() um processo para cada arquivo mv :

find . -name "x*" -print0 | strace xargs -0 -I '{}' mv '{}' /tmp/other

.
.
read(0, "./foobar1/xorgslsala11
find /tmp/ -ctime -1 -name "x*" | xargs -I '{}' mv '{}' ~/play/
./foobar1"..., 4096) = 870 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbb82fad000 open("/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=26066, ...}) = 0 mmap(NULL, 26066, PROT_READ, MAP_SHARED, 3, 0) = 0x7fbb82fa6000 close(3) = 0 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fbb835af9d0) = 661 wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 661 --- SIGCHLD (Child exited) @ 0 (0) --- clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fbb835af9d0) = 662 wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 662 --- SIGCHLD (Child exited) @ 0 (0) --- . . .
    
por 16.09.2013 / 16:18
15

Com ferramentas GNU:

find /tmp/ -ctime -1 -name 'x*' -print0 |
  xargs -r0 mv -t ~/play/

A opção -t ( --target ) é específica do GNU. -print0 , -r , -0 , embora não padrão e originários do GNU também são encontrados em outras implementações, como em alguns BSDs.

POSIXly:

find /tmp/ -ctime -1 -name 'x*' -exec sh -c '
  exec mv "$@" ~/play/' sh {} +

Ambos executam alguns comandos mv conforme necessário e trabalham com quaisquer caracteres que os nomes de arquivos possam conter. O GNU pode ter a vantagem de que find continua procurando por arquivos, enquanto mv começa a mover o primeiro lote.

Cuidado com o fato de que todos os arquivos e diretórios terminarão em um diretório, cuidado com os conflitos se vários arquivos em diretórios diferentes tiverem o mesmo nome.

    
por 16.09.2013 / 17:28