Dosyaları kopyalarken argüman listesi çok uzun


26

Sadece belirli bir uzantıdaki dosyaları nasıl sayabileceğim ile ilgili bir soru sordum . Şimdi cpbu dosyaları yenisiyle istiyorum dir.

Deniyorum,

cp *.prj ../prjshp/

ve

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

ama aynı hatayı veriyorlar

bash: / bin / cp: Bağımsız değişken listesi çok uzun

Onları nasıl kopyalarım?


Yanıtlar:


36

cp *.prj ../prjshp/doğru komuttur, ancak boyut sınırlamasına girdiği nadir bir duruma çarptınız. Çalıştığın ikinci komut hiçbir anlam ifade etmiyor.

Bir yöntem, cptopaklardaki dosyaları çalıştırmaktır . findKomut bunun nasıl bilir:

find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
  • find geçerli dizini ve altındaki dizinleri tekrarlı bir şekilde hareket eder.
  • -maxdepth 1 1 derinlikte durmak anlamına gelir, yani alt dizinlere tekrar girmeyin.
  • -name '*.prj'yalnızca adı belirtilen kalıba uyan dosyalara etki etmek anlamına gelir. Desenin etrafındaki tırnaklara dikkat edin: findkabuk tarafından değil , komut tarafından yorumlanacaktır .
  • -exec … {} +tüm dosyalar için belirtilen komutu yürütmek anlamına gelir. Komut satırı sınırını geçmemeye dikkat ederek, gerekirse komutu defalarca çağırır.
  • mv -t ../prjshpbelirtilen dosyaları içine taşır ../prjshp. -tSeçenek çünkü bir sınırlama nedeniyle burada kullanılan findkomutu: (ile sembolize bulunan dosyalar {}) komutunun son argüman olarak geçirilen, bunun ardından hedef eklemek mümkün değil.

Başka bir yöntem kullanmaktır rsync.

rsync -r --include='*.prj' --exclude='*' . ../prjshp
  • rsync -r … . ../prjshpgeçerli dizini ../prjshpyinelemeli olarak kopyalar .
  • --include='*.prj' --exclude='*'eşleşen dosyaları kopyalamak *.prjve her şeyi hariç tutmak (alt dizinler dahil, bu nedenle .prjalt dizinlerdeki dosyalar bulunamaz) anlamına gelir.

3
rsync, şu ana kadarki en kolay çözüm.
ntk4

Biraz nitpicky olmak gerekirse, ikinci komut cp * | grep '\.prj$' ../prjshp/ bir anlam ifade etmiyor, ancak *sonuncusu bir dizin (aka cp SOURCE1 SOURCE2....DEST) olan dosyaların listesine genişlerse sözdizimsel olarak geçerli olabilir . O olacak - Boru emin, ama aynı zamanda çok kabuk söz konusu olduğunda olarak sözdizimsel olarak geçerli kalır, herhangi bir anlam ifade etmez dup(), bunun nedeni borunun okuyucu ucu herhangi bir veri almazsınız sadece bu gayet dosya tanımlayıcıları var cpherhangi yazmaz .
Sergiy Kolodyazhnyy

Hem bul hem de rsync aynı argüman listesini benim için çok uzun bir hata üretti. For döngüsü en basit geçici çözümdü.
Meezaan-ud-Din

Aslında, rsync, herhangi bir toplu kopyalama yapmanın yoludur, ancak Linux ile ne kadar ileri geldiğimize gerilediysem ve bunun gibi aptal bir kusurumuz / böceğimiz var ve evet, bunun bir kusur / böcek olduğunu düşünürdüm.
MitchellK

22

Bu komut dosyaları birer birer kopyalar ve *tek bir cpkomuta genişletilemeyecek kadar çok olsa bile çalışır :

for i in *; do cp "$i" ../prjshp/; done

Bu benim için çalışıyor.
1rq3fea324wre

1
Basit ve etkili. Bir projeye ait videodan çıkardığım ~ 1/4 milyon jpeji kaldırırken benzer bir sorun yaşadım. Bu benim kullandığım yaklaşım.
Elder Geek

5

Argument list too longHatayla karşılaştığınızda akılda tutulması gereken 3 önemli nokta vardır :

  • Komut satırı argümanlarının uzunluğu, POSIX tanımına göre "... [m] çevre verisi de dahil olmak üzere çalıştırma işlevlerinin maksimum argüman uzunluğu" ARG_MAXolan değişkenle sınırlıdır . -built-it komutu, bu komutun işleminin ortaya çıkması için birini çağırmak zorundadır ve o zaman devreye girer, ayrıca komutun kendisine verilen ad veya yol (örneğin ) bir rol oynar.exec()ARG_MAX/bin/echo

  • Kabuk yerleşik komutları kabuk tarafından yürütülür, yani kabuğun exec()işlev ailesini kullanmadığı ve bu nedenle ARG_MAXdeğişkenden etkilenmediği anlamına gelir .

  • Gibi xargsve değişkenlerinin findfarkında olan bazı komutlar ARG_MAXve art arda bu sınırın altındaki işlemleri gerçekleştirir

Yukarıdaki noktalardan ve Kusalananda'nın konuyla ilgili mükemmel cevabında gösterildiği gibi Argument list too long, çevre büyük olduğunda da ortaya çıkabilir. Bu nedenle, her bir kullanıcının ortamının değişebileceğini ve bayt cinsinden argüman boyutunun alakalı olduğunu dikkate alarak tek bir dosya / argüman bulmak zor.

Böyle bir hata nasıl ele alınır?

Önemli olan dosya sayısına değil, kullanacağınız komutun exec()işlev ailesini ve teğetsel olarak - yığın alanını içerip içermediğine odaklanmaktır .

Kabuk yerleşiklerini kullan

Daha önce tartışıldığı gibi, kabuk yapıları ARG_MAXsınırlamaya karşı bağışıklık kazanır, yani fordöngü, whiledöngü, yerleşik echove yerleşik gibi şeyler printf- hepsi yeterince iyi performans gösterir.

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

On ilgili soruya dosyaları silme hakkında gibi bir çözüm vardı:

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

Bu kabuğun yerleşik kullandığını unutmayın printf. Eğer dış diyoruz printf, buna dahil olacak exec(), dolayısıyla çok sayıda argümanla başarısız olacaksınız:

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

bash dizileri

Göre bir cevap jlliagre göre, bashdanjpreron en gösterildiği gibi dosya isimleri dizi yapı ve halka yineleme başına dilimleri kullanılarak, böylece dizilerin ilgili sınırlar yoktur, sıra yapılabilir cevap :

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

Bununla birlikte, bunun bash-özgü ve POSIX-dışı olma sınırlaması vardır.

Yığın alanını arttır

Bazen insanlar önermek görebilirsiniz yığın alanı artan ile ulimit -s <NUM>; Linux'ta ARG_MAX değeri, her program için yığın alanının 1 / 4'üdür; bu, yığın alanını arttırmanın, bağımsız değişkenler için alanı orantılı olarak artırdığı anlamına gelir.

# 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

Göre Franck Dernoncourt tarafından cevap Linux Journal değinir, bir de gerekenden daha fazla iş var ve belirtilen açıkları için potansiyel açar ancak bağımsız değişkenler için maksimum bellek sayfalarının için büyük değere sahip Linux çekirdeği yeniden derleme Linux Journal makaleye atıf olabilir.

Kabuk önlemek

Başka bir yol, kullanmak pythonveya python3varsayılan olarak Ubuntu ile birlikte gelen kullanmaktır . Aşağıdaki python + here-doc örneği, 40.000 öğe aralığında bir yerde büyük bir dosya dizinini kopyalamak için şahsen kullandığım bir şeydir:

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

Özyinelemeli geçişler için os.walk kullanabilirsiniz .

Ayrıca bakınız:


2

IMHO, dosya ordularıyla uğraşmak için en uygun araçlar findve xargs. Bakın man find. Bakın man xargs. findOnun ile -print0anahtarın, bir üreten NULdosya isimlerinin -separated listesi (dosya, herhangi bir karakter execpt içerebilir NULya da /) xargskullanılarak, anlar -0anahtarı. xargsdaha sonra izin verilen en uzun komutu (en fazla dosya adı, sonunda hiçbir yarı dosya adı yok) oluşturur ve çalıştırır. daha fazla dosya adı xargsgirinceye kadar bunu tekrarlar find. xargs --show-limits </dev/nullSınırları görmek için koş .

Sorununuzu çözmek için (ve man cpbulmak için kontrol ettikten sonra --target-directory=):

find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.