Kesalahan 'Daftar argumen terlalu panjang' saat menyalin sejumlah besar file

12

Saya menggunakan perintah berikut:

\cp -uf /home/ftpuser1/public_html/ftparea/*.jpg /home/ftpuser2/public_html/ftparea/

Dan saya mendapatkan kesalahan:

-bash: /bin/cp: Argument list too long

Saya juga sudah mencoba:

ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} /home/ftpuser2/public_html/ftparea/

Masih punya -bash: / bin / ls: Daftar argumen terlalu panjang

Ide saya?

icelizard
sumber
Saya mencoba menyalin semua jpg dari 1 direktori ke direktori lain tetapi hanya file baru dan yang telah diperbarui.
icelizard
lstidak dirancang untuk melakukan hal semacam ini. Gunakan find.
Dijeda sampai pemberitahuan lebih lanjut.
Masalahnya bukan dengan ls, itu dengan jumlah argumen yang diberikan shell ke ls. Anda akan mendapatkan kesalahan yang sama dengan vi atau dengan perintah non-builtin.
chris
Tapi lsini terutama tidak dirancang untuk melakukan hal ini: mywiki.wooledge.org/ParsingLs
Dihentikan Sementara sampai pemberitahuan lebih lanjut.
Benar, tetapi dalam kasus ini kesalahannya bukan karena kesalahan parsing dengan ls, itu adalah dengan mengirimkan satu miliar argumen ke proses baru yang kebetulan ls. Selain menjadi penggunaan yang tidak tepat dari ls, itu juga kebetulan berbenturan dengan keterbatasan sumber daya / desain unix. Dalam hal ini, pasien mengalami sakit perut dan patah kaki.
chris

Jawaban:

19

* .jpg meluas ke daftar lebih lama daripada yang bisa ditangani shell. Coba ini sebagai gantinya

find  /home/ftpuser/public_html/ftparea/ -name "*.jpg" -exec cp -uf "{}" /your/destination \;
Shawn Chin
sumber
Saya menggunakan find / home / ftpuser1 / public_html / ftparea / -name "* jpg" -exec cp -uf "{}" / home / ftpuser2 / public_html / ftparea / dan mendapatkan pencarian kesalahan berikut: tidak ada argumen untuk `-exec '
icelizard
Anda melewatkan argumen terakhir cp, penjawab memberi tahu Anda dengan benar. Periksa implementasi Anda. Perhatikan bahwa dalam jawaban ini, titik di "* .jpg" tidak ada, ini dapat menyebabkan kesalahan perilaku (misalnya, dir yang bernama "myjpg"). Perhatikan bahwa itu mungkin paranoic tetapi lebih aman untuk menentukan dengan cermat apa yang akan Anda salin menggunakan file -type (mencegah dir, symlink, dan sebagainya yang akan terpengaruh)
drAlberT
Setelah pemeriksaan lebih dekat saya melewatkan "\;" untuk menyelesaikan perintah yang harus dijalankan oleh -exec. Saya konyol!
icelizard
@AlberT: terima kasih untuk kepala kembali titik yang hilang. Itu salah ketik. Jawaban diperbarui.
Shawn Chin
Bukannya cp tidak bisa mengatasinya. Shell tidak bisa.
d -_- b
6

Ada batas maksimum untuk berapa lama daftar argumen bisa untuk perintah sistem - batas ini adalah distro-spesifik berdasarkan nilai MAX_ARG_PAGESketika kernel dikompilasi, dan tidak dapat diubah tanpa mengkompilasi ulang kernel.

Karena cara globbing ditangani oleh shell, ini akan memengaruhi sebagian besar perintah sistem saat Anda menggunakan argumen yang sama ("* .jpg"). Karena glob diproses oleh shell terlebih dahulu, dan kemudian dikirim ke perintah, perintah:

cp -uf *.jpg /targetdir/

pada dasarnya sama dengan shell seolah-olah Anda menulis:

cp -uf 1.jpg 2.jpg ... n-1.jpg n.jpg /targetdir/

Jika Anda berurusan dengan banyak jpeg, ini bisa menjadi tidak terkelola dengan sangat cepat. Bergantung pada konvensi penamaan Anda dan jumlah file yang sebenarnya harus Anda proses, Anda dapat menjalankan perintah cp pada subset direktori yang berbeda secara bersamaan:

cp -uf /sourcedir/[a-m]*.jpg /targetdir/
cp -uf /sourcedir/[n-z]*.jpg /targetdir/

Ini bisa berhasil, tetapi seberapa efektif itu akan didasarkan pada seberapa baik Anda dapat memecah daftar file Anda menjadi blok globbable nyaman.

Globbable. Saya suka kata itu.

Beberapa perintah, seperti find dan xargs , dapat menangani daftar file besar tanpa membuat daftar argumen dengan ukuran yang menyakitkan.

find /sourcedir/ -name '*.jpg' -exec cp -uf {} /targetdir/ \;

Argumen -exec akan menjalankan sisa baris perintah satu kali untuk setiap file yang ditemukan oleh find , menggantikan {} dengan setiap nama file yang ditemukan. Karena perintah cp hanya dijalankan pada satu file pada satu waktu, batas daftar argumen tidak menjadi masalah.

Ini mungkin lambat karena harus memproses setiap file secara individual. Menggunakan xargs dapat memberikan solusi yang lebih efisien:

find /sourcedir/ -name '*.jpg' -print0 | xargs -0 cp -uf -t /destdir/

xargs dapat mengambil daftar file lengkap yang disediakan oleh find , dan memecahnya menjadi daftar argumen ukuran yang dapat dikelola dan menjalankan cp pada masing-masing sublists tersebut.

Tentu saja, ada juga kemungkinan hanya mengkompilasi ulang kernel Anda, menetapkan nilai yang lebih besar MAX_ARG_PAGES. Tetapi mengkompilasi ulang kernel lebih berhasil daripada yang saya ingin jelaskan dalam jawaban ini.

goldpseudo
sumber
Saya tidak tahu mengapa ini dipilih. Itu satu-satunya jawaban yang sepertinya menjelaskan mengapa ini terjadi. Mungkin karena Anda tidak menyarankan menggunakan xargs sebagai optimasi?
chris
menambahkan dalam solusi xargs, tetapi saya masih khawatir downvotes adalah karena sesuatu yang salah secara detail dan tidak ada yang ingin memberi tahu saya apa itu. :(
goldPseudo
xargstampaknya jauh lebih efisien, karena jumlah panggilan perintah yang dihasilkan jauh lebih kecil. Dalam kasus saya, saya melihat kinerja 6-12 kali lebih baik ketika menggunakan argskemudian ketika menggunakan -execsolusi dengan semakin banyak file adalah efisiensi yang semakin meningkat.
Jan Vlcinsky
3

Itu terjadi karena ekspresi wildcard Anda ( *.jpg) melebihi batas panjang argumen baris perintah ketika diperluas (mungkin karena Anda memiliki banyak file .jpg di bawah /home/ftpuser/public_html/ftparea).

Ada beberapa cara untuk menghindari batasan itu, seperti menggunakan findatau xargs. Lihat artikel ini untuk detail lebih lanjut tentang cara melakukannya.

mfriedman
sumber
+1 untuk sumber daya eksternal yang baik dengan subjek.
viam0Zah
3

Seperti yang dikomentari GoldPseudo, ada batasan berapa banyak argumen yang dapat Anda berikan pada proses yang Anda hasilkan. Lihat jawabannya untuk deskripsi yang baik tentang parameter itu.

Anda dapat menghindari masalah dengan tidak melewati proses terlalu banyak argumen atau dengan mengurangi jumlah argumen yang Anda lewati.

A untuk loop di shell, find, dan ls, grep, dan loop sementara semua melakukan hal yang sama dalam situasi ini -

for file in /path/to/directory/*.jpg ; 
do
  rm "$file"
done

dan

find /path/to/directory/ -name '*.jpg' -exec rm  {} \;

dan

ls /path/to/directory/ | 
  grep "\.jpg$" | 
  while
    read file
  do
    rm "$file"
  done

semua memiliki satu program yang membaca direktori (shell itu sendiri, menemukan, dan ls) dan program lain yang benar-benar mengambil satu argumen per eksekusi dan mengulangi seluruh daftar perintah.

Sekarang, ini akan lambat karena rm perlu bercabang dan dieksekusi untuk setiap file yang cocok dengan pola * .jpg.

Di sinilah xargs berperan. xargs mengambil input standar dan untuk setiap N (untuk freebsd secara default 5000) baris, ia memunculkan satu program dengan argumen N. xargs adalah optimasi dari loop di atas karena Anda hanya perlu memotong program 1 / N untuk beralih ke seluruh rangkaian file yang membaca argumen dari baris perintah.

chris
sumber
1

Glob '*' meluas ke terlalu banyak nama file. Gunakan find / home / ftpuser / public_html -name '* .jpg'.

William Pursell
sumber
Temukan dan gema * menghasilkan output yang sama - kuncinya di sini adalah menggunakan xargs tidak hanya melewati semua 1 miliar argumen baris perintah ke perintah yang dicoba shell.
chris
echo * akan gagal jika ada terlalu banyak file, tetapi find akan berhasil. Juga, menggunakan find -exec dengan + sama dengan menggunakan xargs. (Tidak semua menemukan dukungan +, meskipun)
William Pursell
1

Menggunakan +opsi untuk find -execakan sangat mempercepat operasi.

find  /home/ftpuser/public_html/ftparea/ -name "*jpg" -exec cp -uf -t /your/destination "{}" +

The +opsi membutuhkan {}untuk menjadi argumen terakhir sehingga menggunakan -t /your/destination(atau --target-directory=/your/destination) pilihan untuk cpmerek bekerja.

Dari man find:

perintah -exec {} +

          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.

Sunting : mengatur ulang argumen ke cp

Dijeda sampai pemberitahuan lebih lanjut.
sumber
Saya menemukan: argumen yang hilang untuk `-exec '/ home / ftpuser1 / public_html / ftparea / -name' * jpg '-exec cp -uf" {} "/ home / ftpuser2 / public_html / ftparea / +
icelizard
Saya mengatur ulang argumen cpuntuk memperbaiki kesalahan itu.
Dijeda sampai pemberitahuan lebih lanjut.
1

Sepertinya Anda memiliki terlalu banyak *.jpgfile di direktori itu untuk meletakkan semuanya di baris perintah sekaligus. Kamu bisa mencoba:

find /home/ftpuser/public_html/ftparea1 -name '*.jpg' | xargs -I {} cp -uf {} /home/ftpuser/public_html/ftparea2/

Anda mungkin perlu memeriksa man xargsimplementasi Anda untuk melihat apakah -Iswitch sudah benar untuk sistem Anda.

Sebenarnya, apakah Anda benar-benar berniat untuk menyalin file-file itu ke lokasi yang sama dengan yang sudah ada?

Greg Hewgill
sumber
maaf ini adalah dua direktori yang berbeda harus ftpuser1 dan ftpuser2
icelizard
Baru saja mencoba ini: ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} / home / ftpuser2 / public_html / ftparea / Masih punya -bash: / bin / ls: Daftar argumen terlalu panjang
icelizard
Oh, Anda benar, tentu saja lsakan memiliki masalah yang sama! Saya sudah berubah menjadi findtidak mau.
Greg Hewgill
0

Buka folder

cd /home/ftpuser1/public_html/

dan jalankan yang berikut ini:

cp -R ftparea/ /home/ftpuser2/public_html/

Dengan cara ini jika folder 'ftparea' memiliki subfolder, ini mungkin efek negatif jika Anda hanya menginginkan file '* .jpg' dari itu, tetapi jika tidak ada subfolder, pendekatan ini pasti akan jauh lebih cepat daripada menggunakan find dan xargs

pinpinokio
sumber