find + xargs: baris argumen terlalu panjang


21

Saya memiliki garis seperti berikut:

find /foo/bar -name '*.mp4' -print0 | xargs -i {} -0 mv -t /some/path {}

tapi saya mendapat kesalahan berikut:

xargs: argument line too long

Saya bingung. Bukankah penggunaan xargsseharusnya justru membantu dengan masalah ini?

Catatan: Saya tahu bahwa saya dapat menggunakan secara teknis -execdi find, tetapi saya ingin memahami mengapa hal di atas gagal, karena pemahaman saya adalah yang xargsseharusnya tahu bagaimana membagi input menjadi ukuran yang dapat dikelola dengan argumen yang berjalan. Apakah ini tidak benar?

Ini semua dengan zsh.

Jawaban:


11

Nah untuk satu hal -isaklar sudah usang:

-i[replace-str]
     This  option  is a synonym for -Ireplace-str if replace-str is specified. 
     If the replace-str argument is missing, the effect is the same as -I{}. 
     This option is deprecated; use -I instead.

Jadi ketika saya mengubah perintah Anda, ini berhasil:

$ find /foo/bar -name '*.mp4' -print0 | xargs -I{} -0 mv -t /some/path {}

Contoh

$ find . -print0 | xargs -I{} -0 echo {}
.
./.sshmenu
./The GIT version control system.html
./.vim_SO
./.vim_SO/README.txt
./.vim_SO/.git
./.vim_SO/.git/objects
./.vim_SO/.git/objects/pack
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.idx
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.pack
./.vim_SO/.git/objects/info
./.vim_SO/.git/refs
./.vim_SO/.git/refs/tags
...

Penggunaan -I{}

Pendekatan ini tidak boleh digunakan sejak menjalankan perintah ini:

$ find -print0 ... | xargs -I{} -0 ...

secara implisit menyalakan sakelar ini ke xargs, -xdan -L 1. The -L 1mengkonfigurasi xargssehingga memanggil perintah yang Anda inginkan untuk menjalankan file melalui secara tunggal.

Jadi ini mengalahkan tujuan penggunaan di xargssini karena jika Anda memberikannya 1000 file maka akan menjalankan mvperintah 1000 kali.

Jadi pendekatan mana yang harus saya gunakan?

Anda dapat melakukannya menggunakan xargs seperti ini:

$ find /foot/bar/ -name '*.mp4' -print0 | xargs -0 mv -t /some/path

Atau temukan saja semuanya:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +

Terima kasih! Ketika Anda mengatakan "This approach shouldn't be used"pendekatan mana yang harus digunakan, bukan? Apakah "find /foot/bar/ -name '*.csv' -print0 | xargs -0 mv -t some_dir'"akan menjadi solusi yang lebih baik? Jika demikian, bagaimana bisa xargstahu dalam kasus ini di mana dalam mvperintah untuk memberi makan dalam argumen yang didapatnya dari pipa? (Apakah itu selalu menempatkan mereka sebagai yang terakhir?)
Amelio Vazquez-Reina

@ user815423426 - Melakukannya hanya dengan itu find ... -exec ...adalah cara yang lebih baik atau jika Anda ingin menggunakannya xargsjuga find ... | xargs ... mv -t ...tidak masalah. Ya itu selalu menempatkan mereka yang terakhir. Itu sebabnya metode itu membutuhkan -t.
slm

5

Opsi ini -imengambil argumen opsional. Karena Anda memberi spasi setelahnya -i, tidak ada argumen pada -iopsi dan oleh karena itu yang berikutnya -0bukan opsi untuk xargstetapi yang kedua dari 6 operan {} -0 mv -t /some/path {}.

Dengan hanya opsi -i, xargs mengharapkan daftar nama file yang dipisahkan oleh baris baru. Karena mungkin tidak ada baris baru di input, xargs menerima apa yang tampak seperti nama file besar (dengan byte null tertanam, tetapi xargs tidak memeriksa itu). String tunggal ini yang berisi seluruh output findlebih panjang dari panjang baris perintah maksimum, karenanya kesalahan "baris perintah terlalu panjang".

Perintah Anda akan berfungsi -i{}sebagai gantinya -i {}. Atau, Anda bisa menggunakan -I {}: -Imirip dengan -i, tetapi mengambil argumen wajib, sehingga argumen berikutnya yang dilewatkan ke xargsdigunakan sebagai argumen -Iopsi. Lalu argumen setelah itu -0yang diartikan sebagai opsi, dan sebagainya.

Namun, Anda tidak boleh menggunakannya -I {}sama sekali. Menggunakan -Imemiliki tiga efek:

  • -Imematikan pemrosesan penawaran, yang -0sudah melakukannya.
  • -Imengubah string yang akan diganti, tetapi {}merupakan nilai default.
  • -Imenyebabkan perintah dieksekusi secara terpisah untuk setiap catatan input, yang tidak berguna di sini karena perintah Anda ( mv -t) secara khusus dimaksudkan untuk mengatasi beberapa file per permintaan.

Entah jatuh -Idan -isekaligus

find /foo/bar -name '*.mp4' -print0 | xargs -0 mv -t /some/path {}

atau jatuhkan xargs dan gunakan -exec:

find /foo/bar -name '*.mp4' -exec mv -t /some/path {} +

0

Coba gunakan bash untuk loop:

for FILE in *.mp4 ; do rm $FILE ; done

atau jika Anda ingin melihat apa yang terjadi:

for FILE in *.mp4 ; do echo Removing $FILE ; rm $FILE ; done

0

Jika Anda melihat ini saat menggunakan kulit ikan .
Ini terkait dengan bagaimana ikan memperluas tali pengganti{}

Jika Anda menggunakan ikan, Anda harus lolos dari string pengganti \{\}

| xargs -I \{\} echo \{\}

atau gunakan string pengganti yang berbeda

| xargs -I ! echo !
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.