“Mv: Argüman listesi çok uzun” mu?


64

Sıralama gerektiren bir milyondan fazla dosyaya sahip bir klasörüm var, ancak mvbu iletiyi her zaman çıkardığı için gerçekten hiçbir şey yapamıyorum

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

Uzantı içermeyen dosyaları taşımak için bu komutu kullanıyorum:

mv -- !(*.jpg|*.png|*.bmp) targetdir/

Yanıtlar:


82

xargsiş için bir araçtır. Bu ya da findile -exec … {} +. Bu araçlar, bir defada iletilebilecek birçok argümanla birkaç kez komut çalıştırır.

Her iki yöntemin de değişken argüman listesi sonunda olduğunda gerçekleştirmesi daha kolaydır, buradaki durum bu değil: mvhedefin son argümanı . GNU yardımcı programları ile (yani gömülü olmayan Linux veya Cygwin'de), önce hedefi geçmek için -tseçenek mvfaydalıdır.

Dosya adlarının boşlukları veya hiçbiri \"'yoksa, dosya adlarını girdi olarak belirtebilirsiniz xargs( echokomut bir bash yerleşiktir, bu nedenle komut satırı uzunluğu sınırına tabi değildir):

echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir

Varsayılan alıntı formatı yerine boş ayrılmış giriş kullanmak için bu -0seçeneği xargskullanabilirsiniz.

printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir

Alternatif olarak, ile dosya adlarının listesini oluşturabilirsiniz find. Alt dizinlere tekrarlama yapmamak için, kullanın -type d -prune. Listelenen görüntü dosyaları için hiçbir işlem belirtilmediğinden, yalnızca diğer dosyalar taşınır.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec mv -t targetdir/ {} +

(Bu, kabuk joker yöntemlerinden farklı olarak nokta dosyalarını içerir.)

GNU yardımcı programlarına sahip değilseniz, argümanları doğru sırada almak için bir ara kabuk kullanabilirsiniz. Bu yöntem tüm POSIX sistemlerinde çalışır.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec sh -c 'mv "$@" "$0"' targetdir/ {} +

Zsh, sen yükleyebilirsiniz mvyerleşiğini :

setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/

veya izin vermeyi tercih ederseniz mvve diğer isimler harici komutlara atıfta bulunursa :

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/

veya ksh tarzı küre ile:

setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/

Alternatif olarak, GNU mvve kullanarak zargs:

autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/

1
İlk iki komut "-bash:!: Event not found" ifadesini döndürdü ve sonraki ikisi hiçbir dosyayı hiç taşımadı. Bilmeniz gerekirse CentOS 6.5’tenim
Dominique

1
@Dominique Sorunuzda kullandığınız aynı globbing sözdizimini kullandım. Bunu shopt -s extglobetkinleştirmeniz gerekir. findKomutlarda bir adım özledim , onları düzelttim.
Gilles

Bunu "find: invalid expression; find-invalid ifadesi; '-o' ile daha önce hiçbir şeyi olmayan bir ikili işleç kullandınız." Şimdi diğerlerini deneyeceğim.
Dominique,

@Dominique Gönderdiğim findkomutlar (şimdi) çalışıyor. Kopyalama yapıştırırken bir parçayı terk etmiş olmalısınız.
Gilles

Gilles, bulma komutları için neden "değil" operatörünü kullanmıyorsunuz !? Tuhaf takipten daha açık ve anlaşılması kolay -o. Örneğin,! -name '*.jpg' -a ! -name '*.png' -a ! -name '*.bmp'
CivFan 23:15

13

Eğer Linux çekirdeği ile çalışmak yeterli ise

ulimit -s 100000

Bu, Linux çekirdeğinin 10 yıl önce argüman sınırını yığın büyüklüğüne göre değiştirdiği bir yama içerdiği için işe yarayacak: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ taahhüt /? id = b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

Güncelleme: Eğer cesur hissediyorsan, söyleyebilirsin

ulimit -s unlimited

ve yeterli RAM’e sahip olduğunuz sürece, herhangi bir kabuk genişlemesinde sorun olmaz.


Bu bir kesmek. Yığın sınırını neye ayarlayacağınızı nasıl bildiniz? Bu aynı seansta başlatılan diğer işlemleri de etkiler.
Kusalananda

1
Evet, bu bir kesmek. Çoğu zaman bu tür saldırılar bir kereye mahsustur (ne kadar sıklıkla büyük miktarda dosyayı elle taşıyorsunuz?). İşlemin tüm RAM'lerinizi yemeyeceğinden eminseniz, ayarlayabilirsiniz ulimit -s unlimitedve neredeyse sınırsız dosyalar için işe yarar.
Mikko Rantalainen

İle ulimit -s unlimitedgerçek komut satırı limiti 2 ^ 31 ya da 2 GB. ( MAX_ARG_STRLENçekirdek kaynağında.)
Mikko Rantalainen

9

İşletim sisteminin argüman geçiş sınırı, kabuk yorumlayıcısının içinde gerçekleşen genişlemeler için geçerli değildir. Bu yüzden xargsya da kullanmaya ek olarak find, işlemi tek tek mvkomutlara bölmek için basit bir kabuk döngüsü kullanabiliriz :

for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done

Bu sadece POSIX Kabuk Komut Dili özelliklerini ve yardımcı programlarını kullanır. Bu tek astar, gereksiz noktalı virgül kaldırılmış olarak girintili daha açıktır:

for x in *; do
  case "$x" in
    *.jpg|*.png|*.bmp) 
       ;; # nothing
    *) # catch-all case
       mv -- "$x" target
       ;;
  esac
done

Bir milyondan fazla dosyayla, bu durum, gönderilen mvPOSIX findçözümü @ Gilles'i kullanmak yerine yalnızca birkaç kişi yerine, bir milyondan fazla işlem üretecektir. Başka bir deyişle, bu yol pek çok gereksiz CPU kaybına neden olur.
CivFan

@CivFan Başka bir sorun, değiştirilen versiyonun orijinaline eşdeğer olduğuna kendinizi inandırmaktır. Birkaç uzantıyı filtrelemek için genişlemenin casesonucundaki ifadenin *orijinal !(*.jpg|*.png|*.bmp)ifadeye eşdeğer olduğunu görmek kolaydır . findCevap eşdeğer değildir aslında; alt dizinlere iner (Bir -maxdepthyüklem göremiyorum ).
Kaz

-name . -o -type d -prune -oalt dizinlere düşmekten korur. -maxdepthGörünüşe göre POSIX uyumlu değil, ancak benim sayfamda belirtilmiyor find.
CivFan

Revizyon 1'e geri dönülmüştür. Soru, kaynak veya hedef değişkenler hakkında bir şey söylememektedir, bu nedenle bu, cevaba gereksiz yere zarar vermektedir.
Kaz

5

Daha önce önerilenden daha agresif bir çözüm için, çekirdek kaynağınızı çekin ve düzenleyin include/linux/binfmts.h

Boyutunu arttır MAX_ARG_PAGESBu çekirdek programı argümanlar için izin verecektir bellek miktarını artırır 32. daha büyük bir 'dolayısıyla size belirtmek için izin mvveya rmbir milyon dosya veya ne yapıyorsanız için komutu. Yeniden derleyin, yükleyin, yeniden başlatın.

DİKKAT! Bunu sistem belleğiniz için çok büyük olarak ayarladıysanız ve daha sonra çok sayıda argüman içeren bir komut çalıştırırsanız BAD THINGS WAPPEN! Bunu çok kullanıcılı sistemlere yaparken son derece dikkatli olun, kötü niyetli kullanıcıların tüm hafızanızı kullanmalarını kolaylaştırır!

Çekirdeğinizi el ile nasıl yeniden derleyip yeniden kuracağınızı bilmiyorsanız, bu yanıtın şimdilik mevcut olmadığını iddia etmeniz en iyisidir.


5

"$origin"/!(*.jpg|*.png|*.bmp)Catch bloğu yerine daha basit bir çözüm :

for file in "$origin"/!(*.jpg|*.png|*.bmp); do mv -- "$file" "$destination" ; done

@Score_Under'a teşekkürler

Çok satırlı bir komut dosyası için şunları yapabilirsiniz ( düşürülmeden ;önce dikkat edin done):

for file in "$origin"/!(*.jpg|*.png|*.bmp); do        # don't copy types *.jpg|*.png|*.bmp
    mv -- "$file" "$destination" 
done 

Tüm dosyaları taşıyan daha genel bir çözüm yapmak için tek astarı yapabilirsiniz:

for file in "$origin"/*; do mv -- "$file" "$destination" ; done

Girinti yaparsanız bu gibi görünüyor:

for file in "$origin"/*; do
    mv -- "$file" "$destination"
done 

Bu, başlangıçtaki her dosyayı alır ve birer birer hedefe taşır. $fileDosya adlarında boşluk veya başka özel karakterler olması durumunda, tırnak içine alınması gerekir.

İşte mükemmel çalışan bu yöntemin bir örneği

for file in "/Users/william/Pictures/export_folder_111210/"*.jpg; do
    mv -- "$file" "/Users/william/Desktop/southland/landingphotos/";
done

İstenilen şeye daha yakın bir çözüm bulmak için for-loop'taki orijinal küre gibi bir şey kullanabilirsiniz.
Score_Under

Orijinal küre derken ne demek istiyorsun?
Whitecat

Bu biraz şifreli olsaydı Üzgünüm, söz konusu topak atıfta bulundu: !(*.jpg|*.png|*.bmp). Bunu "$origin"/!(*.jpg|*.png|*.bmp)Kaz'ın cevabında kullanılan anahtar ihtiyacını önleyen ve formanın basit gövdesini koruyan globbing ile forma ekleyebilirsin .
Score_Under

Müthiş Puan. Yorumunuzu ekledim ve cevabımı güncelledim.
Whitecat,

3

Bazen sadece küçük bir senaryo yazmak en kolay olanıdır, örneğin Python:

import glob, shutil

for i in glob.glob('*.jpg'):
  shutil.move(i, 'new_dir/' + i)

1

mvBirkaç kez çalıştırmayı sakıncası yoksa hala kullanırken bu kısıtlamayı aşabilirsiniz .

Bölümleri bir kerede taşıyabilirsiniz. Diyelim ki uzun bir alfasayısal dosya adı listeniz vardı.

mv ./subdir/a* ./

Bu işe yarıyor. O zaman başka bir büyük öbek parçala. Birkaç hamle yaptıktan sonra tekrar kullanmaya gidebilirsiniz.mv ./subdir/* ./


0

İşte benim iki kuruş, bunu içine ekle .bash_profile

mv() {
  if [[ -d $1 ]]; then #directory mv
    /bin/mv $1 $2
  elif [[ -f $1 ]]; then #file mv
    /bin/mv $1 $2
  else
    for f in $1
    do
      source_path=$f
      #echo $source_path
      source_file=${source_path##*/}
      #echo $source_file
      destination_path=${2%/} #get rid of trailing forward slash

      echo "Moving $f to $destination_path/$source_file"

      /bin/mv $f $destination_path/$source_file
    done
  fi
}
export -f mv

kullanım

mv '*.jpg' ./destination/
mv '/path/*' ./destination/
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.