"Mv: तर्क सूची को बहुत लंबा" हल करना


64

मेरे पास एक लाख से अधिक फ़ाइलों वाला एक फ़ोल्डर है जिसे छंटनी की आवश्यकता है, लेकिन मैं वास्तव में कुछ भी नहीं कर सकता क्योंकि mvइस संदेश को हर समय आउटपुट करता है

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

मैं एक्सटेंशन-कम फ़ाइलों को स्थानांतरित करने के लिए इस आदेश का उपयोग कर रहा हूं:

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

जवाबों:


82

xargsनौकरी के लिए उपकरण है। वह, या findसाथ -exec … {} +। ये उपकरण कई बार एक कमांड चलाते हैं, जिसमें एक ही बार में कई तर्क दिए जा सकते हैं।

चर तर्क सूची अंत में होने पर दोनों तरीकों को अंजाम देना आसान होता है, जो यहाँ नहीं है: अंतिम तर्क mvगंतव्य है। जीएनयू उपयोगिताओं (यानी गैर-एम्बेडेड लिनक्स या सिग्विन पर) के साथ, गंतव्य को पहले पारित करने के लिए, -tविकल्प mvउपयोगी है।

यदि फ़ाइल के नाम में कोई व्हाट्सएप नहीं है और न ही कोई है \"', तो आप केवल इनपुट के रूप में फ़ाइल नाम प्रदान कर सकते हैं xargs( echoकमांड एक बैश बिलिन है, इसलिए यह कमांड लाइन की लंबाई सीमा के अधीन नहीं है):

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

आप डिफ़ॉल्ट उद्धृत प्रारूप के बजाय नल-सीमांकित इनपुट का उपयोग -0करने के विकल्प का उपयोग कर सकते हैं xargs

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

वैकल्पिक रूप से, आप फ़ाइल नामों की सूची जनरेट कर सकते हैं find। उपनिर्देशिकाओं में पुनरावृत्ति से बचने के लिए, का उपयोग करें -type d -prune। चूंकि सूचीबद्ध छवि फ़ाइलों के लिए कोई कार्रवाई निर्दिष्ट नहीं है, केवल अन्य फाइलें स्थानांतरित की जाती हैं।

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

(इसमें शेल वाइल्डकार्ड विधियों के विपरीत डॉट फाइलें शामिल हैं।)

यदि आपके पास GNU उपयोगिताओं नहीं है, तो आप सही क्रम में तर्क प्राप्त करने के लिए एक मध्यवर्ती शेल का उपयोग कर सकते हैं। यह विधि सभी POSIX सिस्टम पर काम करती है।

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

Zsh में, आप लोड कर सकते हैं mvनिर्मित :

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

या यदि आप जाने देना पसंद करते हैं mvऔर अन्य नाम बाहरी आदेशों का उल्लेख करते रहते हैं:

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

या क्ष-शैली के दस्ताने के साथ:

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

वैकल्पिक रूप से, GNU mvऔर का उपयोग कर zargs:

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

1
पहले दो आदेश "-बश:!: ईवेंट नहीं मिला" और अगले दो ने किसी भी फ़ाइल को स्थानांतरित नहीं किया। मैं CentOS 6.5 पर हूँ अगर आपको पता होना चाहिए
डोमिनिक

1
@ मुख्य मैंने उसी ग्लोबिंग सिंटैक्स का उपयोग किया है जिसका उपयोग आपने अपने प्रश्न में किया था। आपको इसे shopt -s extglobसक्षम करने की आवश्यकता होगी । मुझे findकमांड्स में एक कदम याद आया, मैंने उन्हें ठीक कर दिया है।
गाइल्स

मैं इसे "कमांड: ढूंढ: अमान्य अभिव्यक्ति" के साथ प्राप्त कर रहा हूं, आपने इससे पहले कुछ भी नहीं के साथ एक द्विआधारी ऑपरेटर '-o' का उपयोग किया है। मैं अब अन्य लोगों की कोशिश करेंगे।
डोमिनिक

@Dominique findमेरे द्वारा पोस्ट की गई कमांड (अब) काम करती हैं। कॉपी-पेस्ट करते समय आपने एक हिस्सा छोड़ दिया होगा।
गिल्स

गिल्स, आदेशों को खोजने के लिए, "नहीं" ऑपरेटर का उपयोग क्यों नहीं करते हैं !,? विषम अनुगामी की तुलना में यह अधिक स्पष्ट और समझने में आसान है -o। उदाहरण के लिए,! -name '*.jpg' -a ! -name '*.png' -a ! -name '*.bmp'
CivFan

13

यदि लिनक्स कर्नेल के साथ काम करना पर्याप्त है तो आप बस कर सकते हैं

ulimit -s 100000

यह काम करेगा क्योंकि लिनक्स कर्नेल में लगभग 10 साल पहले एक पैच शामिल था जो स्टैक के आकार के आधार पर तर्क की सीमा को बदल देता है: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ प्रतिबद्ध /? id = b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

अद्यतन: यदि आप बहादुर महसूस करते हैं, तो आप कह सकते हैं

ulimit -s unlimited

और जब तक आपके पास पर्याप्त रैम है, तब तक आप किसी भी शेल विस्तार के साथ ठीक रहेंगे।


वह हैक है। आपको कैसे पता चलेगा कि स्टैक लिमिट को क्या सेट करना है? यह एक ही सत्र में शुरू की गई अन्य प्रक्रियाओं को भी प्रभावित करता है।
Kusalananda

1
हाँ, यह एक हैक है। इस तरह के अधिकांश हैक एक बार बंद हो जाते हैं (कितनी बार आप मैन्युअल रूप से किसी भी तरह से बड़ी मात्रा में फ़ाइलों को स्थानांतरित करते हैं?)। यदि आप सुनिश्चित हैं कि प्रक्रिया आपके सभी रैम को खाने के लिए नहीं जा रही है, तो आप सेट कर सकते हैं ulimit -s unlimitedऔर यह व्यावहारिक रूप से असीमित फ़ाइलों के लिए काम करेगा।
मिक्को रैंटलैनेन

साथ ulimit -s unlimitedवास्तविक कमांड लाइन सीमा 2 ^ 31 या 2 जीबी है। ( MAX_ARG_STRLENकर्नेल स्रोत में।)
मिको रानाल्टेनन

9

ऑपरेटिंग सिस्टम की तर्क पासिंग सीमा शेल इंटरप्रेटर के भीतर होने वाले विस्तार पर लागू नहीं होती है। तो उपयोग करने के अलावा xargsया find, हम बस एक शेल लूप का उपयोग करके अलग-अलग mvकमांड में प्रोसेसिंग को तोड़ सकते हैं :

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

यह केवल POSIX शेल कमांड भाषा सुविधाओं और उपयोगिताओं का उपयोग करता है। यह एक-लाइनर इंडेंटेशन के साथ स्पष्ट है, अनावश्यक अर्धविराम हटा दिए गए हैं:

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

एक मिलियन से अधिक फ़ाइलों के साथ, यह mvपोसिक्स findसमाधान @Gilles पोस्ट करने के लिए केवल कुछ आवश्यक के बजाय एक लाख से अधिक प्रक्रियाओं को स्पॉन करेगा । दूसरे शब्दों में, इस तरह से बहुत सारे अनावश्यक सीपीयू मंथन होते हैं।
सिवान

@CivFan एक अन्य समस्या अपने आप को आश्वस्त कर रही है कि संशोधित संस्करण मूल के बराबर है। यह देखना आसान है कि कई एक्सटेंशनों को फ़िल्टर करने caseके लिए *विस्तार के परिणाम पर बयान मूल !(*.jpg|*.png|*.bmp)अभिव्यक्ति के बराबर है । findजवाब इस तथ्य बराबर नहीं है; यह उपनिर्देशिकाओं में उतरता है (मैं एक -maxdepthविधेय नहीं देखता हूं )।
काज

-name . -o -type d -prune -oउप-निर्देशिकाओं में उतरने से बचाता है। -maxdepthजाहिरा तौर पर POSIX अनुरूप नहीं है, हालांकि यह मेरे findआदमी पृष्ठ में उल्लेख नहीं किया गया है ।
CivFan

संशोधन के लिए वापस लुढ़का 1. सवाल स्रोत या गंतव्य चर के बारे में कुछ नहीं कहता है, इसलिए यह उत्तर के लिए अनावश्यक रूप से जोड़ता है।
कज़

5

पहले की पेशकश की तुलना में अधिक आक्रामक समाधान के लिए, अपने कर्नेल स्रोत को खींचें और संपादित करें include/linux/binfmts.h

MAX_ARG_PAGES32 से बड़े किसी चीज़ का आकार बढ़ाएँ । इससे मेमोरी की मात्रा बढ़ जाती है जिससे कर्नेल प्रोग्राम आर्गुमेंट्स के लिए अनुमति देगा, जिससे आप एक मिलियन फ़ाइलों या जो भी आप कर रहे हैं , उसके लिए अपने कमांड mvया rmकमांड को निर्दिष्ट कर सकते हैं। पुनरावर्ती, स्थापित, रिबूट।

सावधान! यदि आप अपने सिस्टम मेमोरी के लिए इसे बहुत बड़ा सेट करते हैं, और फिर बहुत सारे तर्कों के साथ एक कमांड चलाते हैं BAD THINGS WAP HAPPEN! बहु-उपयोगकर्ता प्रणालियों के लिए ऐसा करने में सतर्क रहें, यह दुर्भावनापूर्ण उपयोगकर्ताओं के लिए आपकी सभी मेमोरी का उपयोग करना आसान बनाता है!

यदि आप नहीं जानते हैं कि मैन्युअल रूप से अपनी कर्नेल को फिर से स्थापित करना और पुन: स्थापित करना कैसे संभव है, तो संभवत: सबसे अच्छा है कि आप इस उत्तर का दिखावा करते हैं जो अभी मौजूद नहीं है।


5

"$origin"/!(*.jpg|*.png|*.bmp)कैच ब्लॉक के बजाय उपयोग करने वाला अधिक सरल समाधान :

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

@Score_Under को धन्यवाद

एक बहु-पंक्ति स्क्रिप्ट के लिए आप निम्न कार्य कर सकते हैं (सूचना दी जा सकती है कि ;इससे पहले कि doneवह गिरा हो):

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

अधिक सामान्यीकृत समाधान करने के लिए जो सभी फ़ाइलों को स्थानांतरित करता है, आप एक-लाइनर कर सकते हैं:

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

यदि आप इंडेंटेशन करते हैं तो ऐसा दिखता है:

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

यह प्रत्येक फ़ाइल को मूल में ले जाता है और उन्हें एक-एक करके गंतव्य तक ले जाता है। यदि $fileफ़ाइल नाम में स्थान या अन्य विशेष वर्ण हैं, तो इसके बारे में उद्धरण आवश्यक हैं।

यहां इस पद्धति का एक उदाहरण है जो पूरी तरह से काम करता है

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

आप फॉर-लूप में मूल ग्लोब जैसी किसी चीज़ का उपयोग कर सकते हैं कि उससे जो मांगा जा रहा है उसका करीब से हल निकाला जा सके।
स्कोर_उंडर

मूल ग्लोब का क्या मतलब है?
व्हाईटकेट

क्षमा करें यदि वह थोड़ा गूढ़ था, तो मैं प्रश्न में ग्लोब का उल्लेख कर रहा था !(*.jpg|*.png|*.bmp):। आप इसे ग्लोबिंग करके अपने फॉर-लूप में जोड़ सकते हैं जो कि "$origin"/!(*.jpg|*.png|*.bmp)काज़ के उत्तर में इस्तेमाल किए जाने वाले स्विच की आवश्यकता को टाल देगा और फॉर-लूप के साधारण बॉडी को रखेगा।
स्कोर_उंडर

बहुत बढ़िया स्कोर। मैंने आपकी टिप्पणी को शामिल किया और अपना उत्तर अपडेट किया।
श्वेतकेतु २ Whit

3

कभी-कभी थोड़ी स्क्रिप्ट लिखना आसान होता है, जैसे कि पायथन में:

import glob, shutil

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

1

mvयदि आप इसे एक दो बार चलाने में कोई आपत्ति नहीं करते हैं, तो आप उस प्रतिबंध का उपयोग कर सकते हैं।

आप एक समय में भागों को स्थानांतरित कर सकते हैं। उदाहरण के लिए मान लीजिए कि आपके पास अल्फ़ान्यूमेरिक फ़ाइल नामों की एक लंबी सूची है।

mv ./subdir/a* ./

यह काम करता है। फिर एक और बड़ा हिस्सा बाहर दस्तक। एक दो चाल के बाद, आप बस उपयोग करने के लिए वापस जा सकते हैंmv ./subdir/* ./


0

यहाँ मेरे दो सेंट हैं, इस में संलग्न करें .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

प्रयोग

mv '*.jpg' ./destination/
mv '/path/*' ./destination/
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.