Mengapa find -exec mv {} ./target/ + tidak berfungsi?

98

Saya ingin tahu persis apa {} \;dan {} \+dan apa yang | xargs ...dilakukan. Harap klarifikasi ini dengan penjelasan.

Di bawah 3 perintah dijalankan dan menghasilkan hasil yang sama tetapi perintah pertama membutuhkan sedikit waktu dan formatnya juga sedikit berbeda.

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

Itu karena yang pertama menjalankan fileperintah untuk setiap file yang berasal dari findperintah. Jadi, pada dasarnya ini berjalan sebagai:

file file1.txt
file file2.txt

Tetapi yang terakhir 2 temukan dengan -execperintah jalankan perintah file sekali untuk semua file seperti di bawah ini:

file file1.txt file2.txt

Kemudian saya menjalankan perintah berikut di mana yang pertama berjalan tanpa masalah tetapi yang kedua memberikan pesan kesalahan.

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

Untuk perintah dengan {} \+, itu memberi saya pesan kesalahan

find: missing argument to `-exec'

mengapa demikian? Adakah yang bisa menjelaskan apa yang saya lakukan salah?

Shahadat Hossain
sumber
pertanyaan sebenarnya sederhana, mengapa yang pertama berhasil dan yang kedua tidak? (1) temukan. -type f -iname ' .cpp' -exec mv {} ./test/ \; (2) temukan. -type f -iname ' .cpp' -exec mv {} ./test/ \ +
Shahadat Hossain

Jawaban:

185

The halaman pengguna (atau secara online pengguna GNU ) cukup banyak menjelaskan semuanya.

temukan perintah -exec {} \;

Untuk setiap hasil, command {}dieksekusi. Semua kemunculan {}diganti dengan nama file. ;diawali dengan garis miring untuk mencegah shell menafsirkannya.

temukan -exec perintah {} +

Setiap hasil ditambahkan commanddan dieksekusi sesudahnya. Dengan mempertimbangkan batasan panjang perintah, saya rasa perintah ini dapat dieksekusi lebih banyak, dengan halaman manual mendukung saya:

jumlah total pemanggilan perintah akan jauh lebih sedikit daripada jumlah file yang cocok.

Perhatikan kutipan ini dari halaman manual:

Baris perintah dibuat dengan cara yang sama seperti xargs membangun baris perintahnya

Itulah mengapa tidak ada karakter yang diperbolehkan di antara {}dan +kecuali spasi. +membuat find mendeteksi bahwa argumen harus ditambahkan ke perintah seperti xargs.

Solusinya

Untungnya, implementasi GNU mvdapat menerima direktori target sebagai argumen, dengan salah satu -tatau parameter yang lebih panjang --target. Penggunaannya adalah:

mv -t target file1 file2 ...

findPerintah Anda menjadi:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

Dari halaman manual:

perintah -exec;

Jalankan perintah; true jika status 0 dikembalikan. Semua argumen berikut untuk ditemukan dianggap argumen untuk perintah sampai argumen yang terdiri dari ';' ditemui. String `{} 'diganti dengan nama file saat ini yang sedang diproses di mana pun itu terjadi dalam argumen ke perintah, tidak hanya dalam argumen yang berdiri sendiri, seperti dalam beberapa versi find. Kedua konstruksi ini mungkin perlu di-escape (dengan `\ ') atau dikutip untuk melindunginya dari perluasan oleh shell. Lihat bagian CONTOH untuk contoh penggunaan opsi -exec. Perintah yang ditentukan dijalankan sekali untuk setiap file yang cocok. Perintah dijalankan di direktori awal. Ada masalah keamanan yang tak terhindarkan seputar penggunaan tindakan -exec; Anda harus menggunakan opsi -execdir sebagai gantinya.

-exec perintah {} +

Varian tindakan -exec ini menjalankan perintah yang ditentukan pada file yang dipilih, tetapi baris perintah dibuat dengan menambahkan setiap nama file yang dipilih di bagian akhir; jumlah total pemanggilan perintah akan jauh lebih sedikit daripada jumlah file yang cocok. Baris perintah dibuat dengan cara yang sama seperti xargs membangun baris perintahnya. Hanya satu kemunculan `{} 'yang diperbolehkan dalam perintah. Perintah dijalankan di direktori awal.

Lekensteyn
sumber
1
Saya tahu sebenarnya cara kerjanya, saya telah membaca manual ini beberapa kali, tetapi saya mendapat pesan kesalahan karena menggunakan {} +, meskipun berfungsi untuk {} \; dan saya menggunakan Cygwin di windows.
Shahadat Hossain
1
@ Shahadat: apakah Anda sudah membaca bagian sebelumnya "Dari halaman manual"? Anda telah menempatkan di ./test/antara {}dan +, tetapi tidak ada karakter non-spasi kosong yang diperbolehkan di antara keduanya.
Lekensteyn
ru mengatakan bahwa saya tidak boleh menempatkan ./test/ di antara {} dan +. Lalu bagaimana perintah mv akan bekerja; mv membutuhkan sumber yaitu {} dan membutuhkan tujuan yaitu ./test/ dan terminasi dengan +. bisa tolong tulis perintah apa yang menurut anda benar?
Shahadat Hossain
@Shahadat: Saya melihat apa yang Anda coba capai. Windows lambat dalam menjalankan program, jadi Anda ingin menggabungkannya menjadi satu perintah. Saya akan menambahkan alternatif untuk jawabannya.
Lekensteyn
1
The +perintah adalah AFAIU aneh sedikit karena tongkat file pada "akhir" (dan bukan di tempat {}) jadi mengapa menggunakan {}sama sekali - ini membingungkan. Terima kasih atas -topsi yang tidak saya ketahui, tampaknya opsi itu dibuat sebagai solusi untuk -exec +masalah itu!
e2-e4
6

Saya mengalami masalah yang sama di Mac OSX , menggunakan shell ZSH : dalam hal ini tidak ada -topsi untuk mv, jadi saya harus mencari solusi lain. Namun perintah berikut berhasil:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

Rahasianya adalah mengutip kawat gigi . Tidak perlu kawat gigi berada di akhir execperintah.

Saya menguji di bawah Ubuntu 14.04 (dengan shell BASH dan ZSH ), itu berfungsi sama.

Namun, saat menggunakan +tanda tersebut, tampaknya memang harus berada di akhir execperintah.

arvymetal.dll
sumber
{}kebutuhan akan dikutip dalam fishdan rckerang, tetapi tidak dalam zsh, bashmaupun kerang lain dari Bourne atau keluarga csh.
Stephane Chazelas
@StephaneChazelas Ya, diuji ulang di bawah Ubuntu dengan bash, memang tanda kutipnya tidak perlu. Anehnya, saya mengalami masalah jika tidak mengutipnya di bawah MacOS (menggunakan zsh). Tapi saya tidak memiliki Mac yang bisa dijangkau untuk mencoba lagi ...
arvymetal
3

Persamaan standar find -iname ... -exec mv -t dest {} +untuk findimplementasi yang tidak mendukung -inameatau mvimplementasi yang tidak mendukung -tadalah menggunakan shell untuk menyusun ulang argumen:

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +

Dengan menggunakan -name '*.[cC][pP][pP]', kami juga menghindari ketergantungan pada lokal saat ini untuk memutuskan apa versi huruf besar catau p.

Perhatikan bahwa +, berlawanan dengan ;is not special di shell manapun jadi tidak perlu dikutip (meskipun mengutip tidak akan merugikan, kecuali tentu saja dengan shell seperti rcitu tidak mendukung \sebagai operator kutipan).

Trailing /di /dest/dir/adalah agar mvgagal dengan kesalahan bukan mengubah nama foo.cppuntuk /dest/dirdalam kasus di mana hanya satu cppfile yang ditemukan dan /dest/dirtidak ada atau tidak direktori (atau symlink ke direktori).

Stephane Chazelas
sumber
+1 ... operasi dalam shell sebagai langkah awal untuk menjalankan perintah sebenarnya berguna untuk berbagai kasus penggunaan ... bagus.
Cbhihe
0
find . -name "*.mp3" -exec mv --target-directory=/home/d0k/Музика/ {} \+
DarkLabs
sumber
Harap tambahkan beberapa penjelasan pada jawaban Anda sehingga orang lain dapat belajar darinya
Nico Haase
Anda perlu menjawab pertanyaan yang meminta penjelasan. Kode-saja bukanlah jawaban.
Lajos Arpad
-1

tidak, perbedaan antara +dan \;harus dibalik. +menambahkan file ke akhir perintah exec kemudian menjalankan perintah exec dan \;menjalankan perintah untuk setiap file.

Masalah yaitu find . -type f -iname '*.cpp' -exec mv {} ./test/ \+harus find . -type f -iname '*.cpp' -exec mv {} ./test/ + ada kebutuhan untuk melarikan diri atau mengakhiri+

xargs Saya sudah lama tidak menggunakannya tapi menurut saya berfungsi seperti +.

Mike Ramirez
sumber
Saya juga mencoba dengan ini tetapi mendapat pesan kesalahan yang sama. Selain itu, di mana pun saya menemukan hanya menggunakan + tetapi di cygwin saya, saya harus menggunakan \ + atau "+" untuk bekerja.
Shahadat Hossain
oh ini adalah lingkungan cygwin. Maaf kalau begitu saya tidak tahu, saya tidak menggunakan shell cygwin, saya hanya menggunakan * nix.
Mike Ramirez
1
@Shahadat Hossain coba -name "*.cpp"Saya hampir tidak menggunakan -iname kecuali jika saya ingin melakukan pencarian regex yang sulit, seperti -iname '??? work. * \. Cpp'
Mike Ramirez
1
@ Mike: Saya pikir Anda salah paham tentang perbedaan antara -inamedan -name. -inameadalah versi peka huruf besar / kecil -namedan tidak memiliki perbedaan dalam penanganan ekspresi reguler. Saya sarankan untuk mencoba perintah sebelum memposting, perintah Anda gagal di shell saya juga.
Lekensteyn
1
@Lekensteyn Itu sudah ditetapkan bahwa itu terjadi sebelum komentar Anda. Saya pikir saya telah mengakui Shahadat sebelum posting Anda, itu sederhana "ok". Tidak, saya tidak menjalankannya secara manual, saya melakukannya dari atas kepala saya dan jarang menggunakan bentuk pencarian regex dengan find. Itu hanya hal jenis 'mungkin membantu'.
Mike Ramirez