Daftar argumen terlalu lama saat menyalin file

26

Saya hanya mengajukan pertanyaan terkait bagaimana saya bisa menghitung file ekstensi tertentu. Sekarang saya ingin cpfile-file ini ke yang baru dir.

Aku sedang mencoba,

cp *.prj ../prjshp/

dan

cp * | grep '\.prj$' ../prjshp/

tetapi mereka memberikan kesalahan yang sama,

bash: / bin / cp: Daftar argumen terlalu panjang

Bagaimana cara menyalinnya?

Sam007
sumber

Jawaban:

36

cp *.prj ../prjshp/adalah perintah yang tepat, tetapi Anda telah menemukan kasus yang jarang terjadi di mana ia mengalami batasan ukuran. Perintah kedua yang Anda coba tidak masuk akal.

Salah satu metode adalah menjalankan cpfile dalam potongan. The findPerintah tahu bagaimana melakukan ini:

find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
  • find melintasi direktori saat ini dan direktori di bawahnya secara rekursif.
  • -maxdepth 1 berarti berhenti pada kedalaman 1, yaitu jangan berulang ke subdirektori.
  • -name '*.prj'berarti hanya bertindak pada file yang namanya cocok dengan pola yang ditentukan. Perhatikan tanda kutip di sekitar pola: itu akan ditafsirkan oleh findperintah, bukan oleh shell.
  • -exec … {} +berarti mengeksekusi perintah yang ditentukan untuk semua file. Itu memanggil perintah beberapa kali jika perlu, berhati-hatilah agar tidak melebihi batas baris perintah.
  • mv -t ../prjshpmemindahkan file yang ditentukan ke ../prjshp. The -topsi digunakan di sini karena keterbatasan dari findperintah: file yang ditemukan (disimbolkan dengan {}) dilewatkan sebagai argumen terakhir dari perintah, Anda tidak dapat menambahkan tujuan setelah.

Metode lain adalah menggunakan rsync.

rsync -r --include='*.prj' --exclude='*' . ../prjshp
  • rsync -r … . ../prjshpmenyalin direktori saat ini ke dalam ../prjshpsecara rekursif.
  • --include='*.prj' --exclude='*'artinya menyalin file yang cocok *.prjdan mengecualikan yang lainnya (termasuk subdirektori, sehingga .prjfile dalam subdirektori tidak akan ditemukan).
Gilles 'SANGAT berhenti menjadi jahat'
sumber
3
rsync, sejauh ini solusi termudah di sini.
ntk4
Untuk menjadi agak nitpicky, perintah kedua cp * | grep '\.prj$' ../prjshp/ tidak masuk akal, tetapi bisa menjadi valid secara sintaksis, jika *diperluas ke daftar file dengan yang terakhir menjadi direktori (alias cp SOURCE1 SOURCE2....DEST). Pipa itu tidak masuk akal, pasti, tetapi juga tetap secara sintaksis valid sejauh menyangkut shell - itu akan dup()deskriptor file baik-baik saja, hanya saja ujung pembaca pipa tidak akan mendapatkan data apa pun karena cptidak menulis apa pun .
Sergiy Kolodyazhnyy
Baik temukan dan rsync menghasilkan daftar argumen yang sama terlalu lama bagi saya. Untuk loop adalah solusi paling sederhana.
Meezaan-ud-Din
Memang rsync adalah cara untuk melakukan penyalinan massal, meskipun saya bingung seberapa jauh kita telah datang dengan Linux dan kita memiliki cacat / bug konyol seperti ini dan ya saya akan menganggapnya cacat / bug.
MitchellK
22

Perintah ini menyalin file satu per satu dan akan bekerja bahkan jika ada terlalu banyak untuk *diperluas menjadi satu cpperintah:

for i in *; do cp "$i" ../prjshp/; done
ccshields
sumber
Ini bekerja untuk saya.
1rq3fea324wre
1
Sederhana dan efektif. Saya memiliki masalah yang sama menghapus ~ 1/4 juta jpegs saya telah diekstraksi dari video untuk proyek. Ini adalah pendekatan yang saya gunakan.
Penatua Geek
5

Ada 3 poin kunci yang perlu diingat ketika menghadapi Argument list too longkesalahan:

  • Panjang argumen baris perintah dibatasi oleh ARG_MAXvariabel, yang menurut definisi POSIX adalah "... [m] panjang argumen maksimum ke fungsi exec termasuk data lingkungan" (penekanan ditambahkan) ". Artinya, ketika shell mengeksekusi -built-it command, ia harus memanggil salah satu exec()untuk menelurkan proses perintah itu, dan di situlah ARG_MAXikut bermain.Selain itu, nama atau jalur ke perintah itu sendiri (misalnya, /bin/echo) memainkan peran.

  • Perintah built-in Shell dijalankan oleh shell, yang berarti shell tidak menggunakan exec()kumpulan fungsi dan karenanya tidak dipengaruhi oleh ARG_MAXvariabel.

  • Perintah tertentu, seperti xargsdan findmenyadari ARG_MAXvariabel dan berulang kali melakukan tindakan di bawah batas itu

Dari poin-poin di atas dan seperti yang ditunjukkan dalam jawaban Kusalananda yang luar biasa pada pertanyaan terkait, hal itu Argument list too longjuga dapat terjadi ketika lingkungan besar. Jadi dengan mempertimbangkan bahwa lingkungan masing-masing pengguna dapat bervariasi, dan ukuran argumen dalam byte relevan, sulit untuk menghasilkan satu file / argumen.

Bagaimana cara mengatasi kesalahan seperti itu?

Kuncinya adalah untuk fokus bukan pada jumlah file, tetapi fokus pada apakah atau tidak perintah yang akan Anda gunakan melibatkan exec()keluarga fungsi dan tangensial - ruang stack.

Gunakan built-in shell

Seperti dibahas sebelumnya, shell built-in kebal terhadap ARG_MAXbatasan, yaitu hal-hal seperti forloop, whileloop, built-in echo, dan built-in printf- semua itu akan berkinerja cukup baik.

for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done

Pada pertanyaan terkait tentang menghapus file, ada solusinya:

printf '%s\0' *.jpg | xargs -0 rm --

Perhatikan bahwa ini menggunakan bawaan shell printf. Jika kita memanggil eksternal printf, itu akan melibatkan exec(), maka akan gagal dengan sejumlah besar argumen:

$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long

array bash

Menurut jawaban oleh jlliagre, bashtidak memberikan batasan pada array, jadi membangun array nama file dan menggunakan irisan per iterasi loop dapat dilakukan juga, seperti yang ditunjukkan dalam jawaban danjpreron :

files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do 
    cp -t /path/to/new_dir/ "${files[@]:I:1000}" 
done

Ini, bagaimanapun, memiliki keterbatasan menjadi spesifik-bash dan non-POSIX.

Tingkatkan ruang tumpukan

Kadang-kadang Anda bisa melihat orang-orang menyarankan meningkatkan ruang stack dengan ulimit -s <NUM>; pada Linux, nilai ARG_MAX adalah 1/4 ruang stack untuk setiap program, yang berarti meningkatkan ruang stack secara proporsional menambah ruang untuk argumen.

# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $((  $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304

Menurut jawaban oleh Franck Dernoncourt , yang mengutip Linux Journal, seseorang juga dapat mengkompilasi ulang kernel Linux dengan nilai yang lebih besar untuk halaman memori maksimum untuk argumen, namun, itu lebih berfungsi daripada yang diperlukan dan membuka potensi untuk dieksploitasi seperti yang dinyatakan dalam artikel Linux Journal yang dikutip.

Hindari cangkang

Cara lain, adalah menggunakan pythonatau python3yang datang secara default dengan Ubuntu. Contoh python + here-doc di bawah ini, adalah sesuatu yang saya pribadi gunakan untuk menyalin direktori besar file di suatu tempat di kisaran 40.000 item:

$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
>    if os.path.isfile(f):
>         shutil.copy(f,'./newdir/')
> EOF

Untuk lintasan rekursif, Anda dapat menggunakan os.walk .

Lihat juga:

Sergiy Kolodyazhnyy
sumber
2

IMHO, alat optimal untuk menangani gerombolan file adalah finddan xargs. Lihat man find. Lihat man xargs. find, dengan -print0switch -nya , menghasilkan NULdaftar-nama file yang terpisah (nama file dapat berisi karakter apa saja NULatau /) yang xargsmengerti, menggunakan -0switch. xargskemudian membangun perintah terlama yang diizinkan (nama file terbanyak, tidak ada nama file setengah di akhir) dan menjalankannya. xargsulangi ini sampai findpersediaan tidak ada lagi nama file. Jalankan xargs --show-limits </dev/nulluntuk melihat batasnya.

Untuk mengatasi masalah Anda, (dan setelah memeriksa man cpuntuk menemukan --target-directory=):

find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
waltinator
sumber