बैश के साथ फ़ाइलों का नाम बदलने बैच


101

बैश अपने संस्करण संख्या को हटाने के लिए पैकेजों की एक श्रृंखला का नाम कैसे बदल सकता है? मैं दोनों के साथ exprऔर %%कोई फायदा नहीं हुआ।

उदाहरण:

Xft2-2.1.13.pkg हो जाता है Xft2.pkg

jasper-1.900.1.pkg हो जाता है jasper.pkg

xorg-libXrandr-1.2.3.pkg हो जाता है xorg-libXrandr.pkg


1
मैं इसे नियमित रूप से लिखने-एक बार उपयोग करने वाली स्क्रिप्ट के रूप में उपयोग करने का इरादा रखता हूं। मैं जिस भी सिस्टम का उपयोग कर रहा हूं, उस पर बैश होगा, इसलिए मैं उन बैश से डरता नहीं हूं जो काफी काम आते हैं।
जेरेमी एल

अधिक सामान्यतः देखें stackoverflow.com/questions/28725333/…
tripleee

जवाबों:


190

आप बैश के पैरामीटर विस्तार सुविधा का उपयोग कर सकते हैं

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done

रिक्त स्थान के साथ फ़ाइल नाम के लिए उद्धरण आवश्यक हैं।


1
मुझे भी यह पसंद है, लेकिन यह बैश-विशिष्ट सुविधाओं का उपयोग करता है ... मैं आमतौर पर "मानक" श के पक्ष में उनका उपयोग नहीं करता हूं।
डिएगो सेविला

2
थ्रोअवे एक-लाइन इंटरेक्टिव सामान के लिए मैं हमेशा सेड-फू के बजाय इसका उपयोग करता हूं। यदि यह एक स्क्रिप्ट थी, तो हाँ, बशियों से बचें।
रिक्टर

एक समस्या मुझे कोड के साथ मिली है: यदि फ़ाइलों का नाम पहले ही बदल दिया गया था और मैं इसे फिर से चलाता हूं तो क्या होगा? मुझे स्वयं के एक उपनिर्देशिका में जाने की कोशिश करने के लिए चेतावनी मिलती है।
जेरेमी एल

1
पुनः चलाने की त्रुटियों से बचने के लिए, आप " .pkg" भाग में समान पैटर्न का उपयोग कर सकते हैं , अर्थात "i में * के लिए [[0-9।] .Pkg; mv $ i $ {i / - [0-9; ।] *। pkg / .pkg}; किया "। लेकिन त्रुटियां पर्याप्त रूप से सहज हैं (एक ही फ़ाइल में चलती हैं)।
ऋचाक

3
एक बैश समाधान नहीं है, लेकिन अगर आपने पर्ल स्थापित किया है, तो यह बैच के नाम बदलने के लिए आसान नाम बदलें कमांड प्रदान करता है, बसrename "s/-[0-9.]*//" *.pkg
Rufus

33

यदि सभी फाइलें अनुक्रम में समान हैं

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh

अपना काम करोगे Sed आदेश के अनुक्रम बनाएगा mv , आदेशों जो आप कर सकते हैं तो खोल में पाइप। यह सबसे अच्छा है कि पहले पाइपलाइन को बिना ट्रेलिंग के चलाया | shजाए ताकि यह सत्यापित किया जा सके कि कमांड आपको क्या चाहिए।

कई निर्देशिकाओं के माध्यम से पुनरावृत्ति करने के लिए कुछ का उपयोग करें

find . -type f |
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' |
sh

ध्यान दें कि में sed नियमित अभिव्यक्ति अनुक्रम समूहीकरण एक बैकस्लैश से पहले कोष्ठक है, \(और \)नहीं बल्कि एक कोष्ठक से, (और )


मेरे भोलेपन को क्षमा करें, लेकिन कोष्ठकों से बचने से पीछे नहीं हटेंगे क्योंकि उन्हें शाब्दिक चरित्र माना जाएगा?
devios1

2
Sed में नियमित अभिव्यक्ति समूहीकरण अनुक्रम है (एकल ब्रैकेट के बजाय।
डायोमिडिस स्पिनेलिस

मेरे लिए काम नहीं किया, आपकी मदद पाने के लिए खुशी होगी .... FROM फ़ाइल का प्रत्यय TO फ़ाइल में जोड़ा गया है: $ मिल। -प्रकार d | sed -n 's /(.*)\/(.*) anysoftkeyboard (। *) / mv "\ 1" / \ 2anysoftkeyboard \ 3 "" \ 1 / \ 2effectedkeyboard "3" / p'। sh >> | >>>>> OUTPUT >>>> mv: rename ./base/src/main/java/com/anysoftkeyboard/base/d शब्दकोशों to ./base/src/main/java/com/effectedkeyboard/base/d शब्दकोशों/d शब्‍द। : ऐसी कोई फ़ाइल या निर्देशिका नहीं
विटाली पोम

आपको कोष्ठक में बैकस्लैश याद आ रहे हैं।
डायोमिडिस स्पिनेलिस

1
lsलिपियों में उपयोग न करें । पहली किस्म को printf '%s\n' * | sed ...कुछ रचनात्मकता के साथ प्राप्त किया जा सकता है और आप शायद इससे छुटकारा sedभी पा सकते हैं। बैश printfएक '%q'प्रारूप कोड प्रदान करता है, जो आपके पास फाइलनाम होने पर काम आ सकता है जिसमें शाब्दिक शेल मेटाचैकर शामिल हैं।
ट्रिपल

10

मैं ऐसा कुछ करूँगा:

for file in *.pkg ; do
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done

माना जाता है कि आपकी सभी फ़ाइल वर्तमान निर्देशिका में हैं। यदि नहीं, तो जेवियर द्वारा ऊपर बताए अनुसार उपयोग करने का प्रयास करें।

संपादित करें : इसके अलावा, यह संस्करण किसी भी बश-विशिष्ट विशेषताओं का उपयोग नहीं करता है, जैसा कि ऊपर के अन्य, जो आपको अधिक पोर्टेबिलिटी की ओर ले जाता है।


वे सभी एक ही डायरेक्टरी में हैं। Rq के उत्तर से आप क्या समझते हैं?
जेरेमी एल

मुझे बैश-विशिष्ट सुविधाओं का उपयोग नहीं करना पसंद है, लेकिन इसके बजाय अधिक मानक श।
डिएगो सेविला

1
मुझे लगता है कि यह एक त्वरित नाम बदलने के लिए था, अगली पीढ़ी के क्रॉस प्लेटफॉर्म, पोर्टेबल एंटरप्राइज एप्लीकेशन लिखने के लिए नहीं ;-)
रिचैक

1
हाहा, काफी हद तक ... मेरे जैसे एक पोर्टेबिलिटी-दिमाग वाले आदमी से बाहर :)
डिएगो सेविला

यह तीसरा उदाहरण के लिए खाता नहीं है: xorg-libXrandr (नाम में इस्तेमाल किया गया हाइफ़न)।
जेरेमी एल

5

यहाँ वर्तमान में स्वीकृत उत्तर के पास एक POSIX है । यह बैश-केवल ${variable/substring/replacement}पैरामीटर विस्तार को ट्रेड करता है जो किसी भी बॉर्न-संगत शेल में उपलब्ध है।

for i in ./*.pkg; do
    mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done

पैरामीटर विस्तार किसी भी प्रत्यय ${variable%pattern}के मूल्य का उत्पादन करता है variableजो patternहटाए गए मेल खाता है। ( ${variable#pattern}एक उपसर्ग को हटाने के लिए भी है।)

मैंने -[0-9.]*स्वीकार किए गए उत्तर से उपपट्ट रखा, हालांकि यह शायद भ्रामक है। यह एक नियमित अभिव्यक्ति नहीं है, लेकिन एक ग्लोब पैटर्न है; तो इसका मतलब यह नहीं है कि "शून्य या अधिक संख्या या डॉट्स के बाद एक डैश"। इसके बजाय, इसका मतलब है "एक पानी का छींटा, उसके बाद एक नंबर या एक डॉट, उसके बाद कुछ भी"। "कुछ भी" सबसे कम संभव मैच होगा, सबसे लंबा नहीं। (बैश प्रदान करता है ##और %%कम से कम के बजाय सबसे लंबे समय तक संभव उपसर्ग या प्रत्यय ट्रिम करने के लिए।)


5

हम मान सकते हैं कि sedयह किसी भी * निक्स पर उपलब्ध है, लेकिन हम यह सुनिश्चित नहीं कर सकते हैं कि यह sed -nmv कमांड उत्पन्न करने के लिए समर्थन करेगा। ( नोट: केवल GNU sedऐसा करता है।)

फिर भी, बिल्ड और सेड को बैश करें, हम ऐसा करने के लिए जल्दी से एक शेल फ़ंक्शन को व्हिप कर सकते हैं।

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      mv -v "$file" "$(sed $sed_pattern <<< $file)"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

प्रयोग

sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg

before:

./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg

after:

./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg

लक्ष्य फ़ोल्डर बनाना:

चूंकि mvहम स्वचालित रूप से लक्ष्य फ़ोल्डर नहीं बनाते हैं, इसलिए हम अपने प्रारंभिक संस्करण का उपयोग नहीं कर सकते हैं sedrename

यह काफी छोटा परिवर्तन है, इसलिए उस सुविधा को शामिल करना अच्छा होगा:

हम एक उपयोगिता समारोह की आवश्यकता होगी, abspath(या पूर्ण पथ) क्योंकि bash में यह निर्माण नहीं है।

abspath () { case "$1" in
               /*)printf "%s\n" "$1";;
               *)printf "%s\n" "$PWD/$1";;
             esac; }

एक बार हमारे पास है कि हम एक फ़ोल्डर / नाम बदलने के लिए लक्ष्य फ़ोल्डर (ओं) को उत्पन्न कर सकते हैं जिसमें नया फ़ोल्डर संरचना शामिल है।

यह सुनिश्चित करेगा कि हम अपने लक्ष्य फ़ोल्डर के नाम जानते हैं। जब हम नाम बदलते हैं तो हमें लक्ष्य फ़ाइल नाम पर इसका उपयोग करने की आवश्यकता होगी।

# generate the rename target
target="$(sed $sed_pattern <<< $file)"

# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"

# finally move the file to the target name/folders
mv -v "$file" "$target"

यहां जानिए पूरा फोल्डर जागरूक स्क्रिप्ट ...

sedrename() {
  if [ $# -gt 1 ]; then
    sed_pattern=$1
    shift
    for file in $(ls $@); do
      target="$(sed $sed_pattern <<< $file)"
      mkdir -p "$(dirname $(abspath $target))"
      mv -v "$file" "$target"
    done
  else
    echo "usage: $0 sed_pattern files..."
  fi
}

बेशक, यह तब भी काम करता है जब हमारे पास विशिष्ट लक्ष्य फ़ोल्डर भी नहीं होते हैं।

यदि हम सभी गीतों को एक फ़ोल्डर में रखना चाहते हैं, ./Beethoven/तो हम यह कर सकते हैं:

प्रयोग

sedrename 's|Beethoven - |Beethoven/|g' *.mp3

before:

./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3

after:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

बोनस दौर ...

एक फ़ोल्डर में फ़ाइलों को फ़ोल्डर से स्थानांतरित करने के लिए इस स्क्रिप्ट का उपयोग करना:

यह मानते हुए कि हम मिलान की गई सभी फ़ाइलों को इकट्ठा करना चाहते हैं, और उन्हें वर्तमान फ़ोल्डर में रखें, हम यह कर सकते हैं:

sedrename 's|.*/||' **/*.mp3

before:

./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3

after:

./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3

Sed रेगेक्स पैटर्न पर ध्यान दें

इस स्क्रिप्ट में नियमित सेड पैटर्न नियम लागू होते हैं, ये पैटर्न पीसीआरई (पर्ल कम्पेटिबल रेगुलर एक्सप्रेशंस) नहीं हैं। आपके प्लेटफॉर्म पर sed -rया तो या sed -Eआपके आधार पर , आप एक्सटेंडेड रेगुलर एक्सप्रेशन सिंटैक्स कर सकते हैं।

POSIX अनुपालन man re_formatको सीड बेसिक और विस्तारित रेगेक्स पैटर्न के पूर्ण विवरण के लिए देखें ।


4

मुझे लगता है कि इस तरह की चीज़ के लिए नाम बदलने के लिए एक अधिक सरल उपकरण है। मैंने इसे OSX के लिए होमब्रे पर पाया

आपके उदाहरण के लिए मैं करूँगा:

rename 's/\d*?\.\d*?\.\d*?//' *.pkg

'S' का अर्थ है विकल्प। प्रपत्र s / searchPattern / प्रतिस्थापन / files_to_apply है। आपको इसके लिए regex का उपयोग करने की आवश्यकता है जो थोड़ा अध्ययन करता है लेकिन यह अच्छी तरह से प्रयास के लायक है।


मैं सहमत हूँ। एक सामयिक चीज के लिए, उपयोग करना forऔर sedआदि ठीक है, लेकिन यह बहुत सरल और तेज है कि renameयदि आप खुद को अक्सर ऐसा करते हुए पाते हैं।
argentum2f

क्या आप पीरियड्स से बच रही हैं?
बिग मनी

समस्या यह है कि यह पोर्टेबल नहीं है; न केवल सभी प्लेटफार्मों में एक renameकमांड नहीं है; इसके अलावा, कुछ में अलग- rename अलग सुविधाओं, वाक्यविन्यास और / या विकल्पों के साथ एक अलग कमांड होगी । यह पर्ल की तरह दिखता है renameजो काफी लोकप्रिय है; लेकिन जवाब वास्तव में यह स्पष्ट करना चाहिए।
tripleee

3

इसके लिए बेहतर उपयोग sed, कुछ इस तरह:

find . -type f -name "*.pkg" |
 sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
 while read nameA nameB; do
    mv $nameA $nameB;
 done

नियमित अभिव्यक्ति को एक अभ्यास के रूप में छोड़ दिया जाता है (जैसा कि उन फ़ाइलनामों से निपटना है जिनमें रिक्त स्थान शामिल हैं)


धन्यवाद: नामों में रिक्त स्थान का उपयोग नहीं किया जाता है।
जेरेमी एल

1

ऐसा लगता है कि काम करने के लिए

  • सब कुछ $ pkg के साथ समाप्त होता है
  • आपका संस्करण # हमेशा "-" के साथ शुरू होता है

.pkg से स्ट्रिप, फिर स्ट्रिप ऑफ - ।।

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done

कुछ पैकेज हैं जिनके नाम में एक हाइफ़न है (तीसरा उदाहरण देखें)। क्या यह आपके कोड को प्रभावित करता है?
जेरेमी एल

1
आप कई सेडल एक्सप्रेशन का उपयोग करके चला सकते हैं -e। जैसे sed -e 's/\.pkg//g' -e 's/-.*//g'
tacotuesday

lsआउटपुट को पार्स न करें और अपने चर को उद्धृत करें। शेल स्क्रिप्ट में सामान्य समस्याओं और एंटीपार्टर्न के निदान के लिए shellcheck.net भी देखें ।
ट्रिपल

1

मेरे पास एक ही फ़ोल्डर में *.txtनाम बदलने के लिए कई फाइलें थीं .sql। नीचे मेरे लिए काम किया:

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done

2
आप अपने उत्तर को ओपी के वास्तविक उपयोग-मामले में अमूर्त करके सुधार सकते हैं।
डेकाब

इसमें कई समस्याओं के साथ-साथ एक स्पष्ट वाक्यविन्यास त्रुटि भी है। एक शुरुआत के लिए shellcheck.net देखें ।
त्रिवेणी

0

इस उत्तर के लिए धन्यवाद। मुझे भी किसी प्रकार की समस्या थी। .Nzb फ़ाइलों के लिए .nzb.queued फ़ाइलों को ले जाना। यह फ़ाइल नाम में स्थान और अन्य cruft था और इससे मेरी समस्या हल हो गई:

find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh

यह डायोमिडिस स्पिनेलिस के उत्तर पर आधारित है।

रेगेक्स पूरे फ़ाइलनाम के लिए एक समूह बनाता है, और पहले .nzb.queued और फिर शेल चाल कमांड बनाता है। के साथ उद्धृत तार। यह शेल स्क्रिप्ट में लूप बनाने से भी बचता है क्योंकि यह पहले से ही सेड द्वारा किया जाता है।


यह ध्यान दिया जाना चाहिए कि यह केवल GNU सेड के लिए सही है। BSDs पर sed -nउसी तरह से विकल्प की व्याख्या नहीं करेगा ।
ओखोडो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.