Bagaimana cara mengintegrasikan perintah mv setelah menemukan perintah?

61

Saya mencari file dengan nama yang ada di AAAdalam jalurnya menggunakan perintah berikut:

find path_A -name "*AAA*"

Mengingat output yang ditunjukkan oleh perintah di atas, saya ingin memindahkan file-file itu ke jalur lain, katakanlah path_B. Alih-alih memindahkan file itu satu per satu, dapatkah saya mengoptimalkan perintah dengan memindahkan file-file itu tepat setelah perintah find?

huahsin68
sumber

Jawaban:

87

Dengan GNU mv :

find path_A -name '*AAA*' -exec mv -t path_B {} +

Itu akan menggunakan -execopsi find yang menggantikan {}dengan setiap hasil find secara bergantian dan menjalankan perintah yang Anda berikan. Sebagaimana dijelaskan dalam man find:

   -exec command ;
          Execute  command;  true  if 0 status is returned.  All following
          arguments to find are taken to be arguments to the command until
          an  argument  consisting of `;' is encountered.  

Dalam hal ini, kami menggunakan +versi -execsehingga kami menjalankan sesedikit mvmungkin operasi:

   -exec command {} +
          This  variant  of the -exec action runs the specified command on
          the selected files, but the command line is built  by  appending
          each  selected file name at the end; the total number of invoca‐
          tions of the command will  be  much  less  than  the  number  of
          matched  files.   The command line is built in much the same way
          that xargs builds its command lines.  Only one instance of  `{}'
          is  allowed  within the command.  The command is executed in the
          starting directory.
cuonglm
sumber
Jangan lupakan "\;" di akhir eksekutif!
Charles Roth
@ Charles Roth: itu +yang menghantam pekerjaan, Anda dapat membaca kutipan saya di atas atau man findsebagai gantinya
cuonglm
Terima kasih untuk ini! Saya mencoba untuk menggunakan -exec mv {} path_b +dan gagal dengan kesalahan izin. TBH, saya masih tidak mengerti mengapa, tetapi -exec mv -t path_b {} +berhasil!
Jeremy Davis
6
@JeremyDavis Saat menggunakan -exec ... {} +, {}harus menjadi hal terakhir sebelum +. Ini sebabnya dia menggunakan mv -t destdir {} +dan tidak mv {} destdir +. Satu dingin digunakan -exec mv {} destdir ';'sebagai gantinya, tetapi itu akan dieksekusi mvsekali untuk setiap file.
Kusalananda
23

Anda dapat melakukan sesuatu seperti di bawah ini juga.

find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B

Dimana,

  1. -0Jika ada spasi atau karakter kosong (termasuk baris baru) banyak perintah tidak akan berfungsi. Opsi ini menangani nama file dengan ruang kosong.
  2. -IGanti kemunculan pengganti-str dalam argumen-awal dengan nama yang dibaca dari input standar. Juga, blanko tanda kutip tidak menghentikan item input; sebaliknya pemisah adalah karakter baris baru.

Pengujian

Saya membuat dua direktori sebagai sourcedirdan destdir. Sekarang, saya membuat banyak file di dalamnya sourcedirsebagai file1.bak, file2.bakdanfile3 with spaces.bak

Sekarang, saya menjalankan perintah sebagai,

find . -name "*.bak" -print0 | xargs -0 -I {} mv {} /destdir/

Sekarang, di dalam destdir, ketika saya melakukannya ls, saya bisa melihat bahwa file telah pindah dari sourcedirke destdir.

Referensi

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/

Ramesh
sumber
22

Untuk kepentingan pengguna OS X menemukan pertanyaan ini, sintaks di OS X sedikit berbeda. Anggap Anda tidak ingin mencari secara rekursif di subdirektori dari path_A:

find path_A -maxdepth 1 -name "*AAA*" -exec mv {} path_B \;

Jika Anda ingin mencari semua file secara rekursif di path_A:

find path_A -name "*AAA*" -exec mv {} path_B \;
mannykary
sumber
Ini bukan sintaks khusus OS X, sama dengan yang findsaya gunakan. Poin bagus: -maxdepth(terutama jika path_B adalah subdirektori - hindari mvmencoba memindahkan file yang sudah ada!) Dan menggunakan \; (jadi {} tidak harus menjadi parameter terakhir dan mvsintaks normal dapat digunakan)
drevicko
6

Ini -execadalah cara terbaik untuk melakukan ini. Jika, karena alasan apa pun, ini bukan opsi, Anda juga dapat membaca hasilnya dalam satu lingkaran:

find path_A -name "*AAA*" -print0 | 
    while IFS= read -r -d $'\0' file; do mv "$file" path_B; done

Itu cara yang aman, dapat menangani nama file yang berisi spasi, baris baru atau karakter aneh lainnya. Cara yang lebih sederhana, tetapi yang gagal kecuali nama file Anda hanya terdiri dari karakter alfanumerik sederhana , adalah

mv $(find path_A -name "*AAA*") path_B

Tapi gunakan loop sementara.

terdon
sumber
Cara sederhana masih bisa gagal jika your file names consist only of simple alphanumeric characters, misalnya jika mencapaiARG_MAX