खोजने के लिए कई फ़ाइलों का नाम कैसे बदलें


37

मैं कई फ़ाइलों का नाम बदलना चाहता हूं (फ़ाइल 1 ... फ़ाइल का नाम फ़ाइल1_renamed ... फ़ाइल नाम = कमांड का उपयोग करके) find:

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'

लेकिन यह त्रुटि प्राप्त करना:

mv: cannot stat filename=./file1’: No such file or directory

यह काम नहीं कर रहा है क्योंकि फ़ाइल नाम को शेल चर के रूप में व्याख्या नहीं किया गया है।

जवाबों:


46

निम्नलिखित आपके दृष्टिकोण का प्रत्यक्ष निर्धारण है:

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;

हालाँकि, यह बहुत महंगा है यदि आपके पास बहुत सारी मेलिंग फाइलें हैं, क्योंकि आप mvप्रत्येक मैच के लिए एक ताजा शेल (जो निष्पादित करते हैं ) शुरू करते हैं। और अगर आपके पास किसी भी फ़ाइल नाम में अजीब अक्षर हैं, तो यह फट जाएगा। एक अधिक कुशल और सुरक्षित तरीका यह है:

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed

इसमें अजीब नाम वाली फाइलों के साथ काम करने का भी फायदा है। यदि findयह समर्थन करता है, तो इसे कम किया जा सकता है

find . -type f -name 'file*' -exec mv {} {}_renamed \;

xargsजब का उपयोग नहीं कर संस्करण उपयोगी है {}के रूप में,

find .... -print0 | xargs --null rm

यहां rmएक बार (या कई बार बहुत सारी फाइलों के साथ) कॉल किया जाता है, लेकिन हर फाइल के लिए नहीं।

मैं हटा दिया basenameआप में सवाल है, क्योंकि यह शायद गलत है: आप कदम होगा foo/bar/file8करने के लिए file8_renamed, नहीं foo/bar/file8_renamed

संपादन (जैसा कि टिप्पणियों में सुझाया गया है):

  • findबिना जोड़ा छोटाxargs
  • जोड़ा गया सुरक्षा स्टिकर

मामले में xबेकार है: find . -type f -name 'file*' -exec mv {} "{}_renamed" \; xargsसंस्करण में पहले उदाहरण की तरह ही दक्षता है /
कोस्टस

xवहाँ केवल करने के लिए सीधे प्रश्नकर्ता के दृष्टिकोण को ठीक।
थॉमस एकर

2
(1) {}शेल ( sh -c "…"कमांड) में सीधे उपयोग करना बहुत खतरनाक है - आपको इसे हमेशा एक तर्क के रूप में पारित करना चाहिए। (2) निर्माण के findसमर्थन के सभी संस्करण नहीं {}_renamed। (३) मैं आपके कथन को नहीं समझता जो xargsफाइलों को हटाने के लिए उपयोगी है (उनका नाम बदलने के विपरीत)।
जी-मैन ने

@ जी-मैन: के साथ अंतर बनाम xargsनहीं है , लेकिन बिना बनाम का उपयोग । पूर्व जैसा है जबकि उत्तरार्द्ध है । mvrm{}mv file1 file1_renamed; mv file2 file2_renamedrm file1 file2
थॉमस इरकर

1
और अगर आप _renamed फ़ाइलों को उनके मूल नामों में बदलना चाहते हैं, तो आप ऐसा कैसे करेंगे?
mcmillab

17

पहले उत्तर की कोशिश करने और इसके साथ थोड़ा करने के बाद मैंने पाया कि इसका उपयोग करके इसे थोड़ा कम और कम जटिल किया जा सकता है -execdir:

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'

लगता है कि यह भी वही होना चाहिए जो आपको चाहिए।


2
findकार्यान्वयन के साथ जो समर्थन करते हैं -execdirऔर {}समग्र रूप से नहीं, यह सबसे सुरक्षित भी है। आप एक जोड़ सकते हैं -iकरने के लिए mvहालांकि (और -Tयदि आपका mvयह समर्थन करता है)
स्टीफन Chazelas

1
@ StéphaneChazelas और भी बेहतर, mvएक संकेत के लिए, या इसके अलावा पर निर्भर होने के बजाय , आप कर सकते हैं (इस पर कोई संदेह नहीं है कि क्या आपका findसमर्थन इसे लागू करता है) भी इसका उपयोग -okdirकरेगा जो इसे निष्पादित करने से पहले कमांड को निष्पादित करने के लिए आउटपुट देगा।
Matijs

-depthयदि आप भी निर्देशिका नामों को छूने जा रहे हैं, तो यह उल्लेख करना एक अच्छा विचार है।
सिरो सेंटिल्ली 新疆 改造 iro 六四

अरे, बस में पाया गया कि -execdirएक बहुत कष्टप्रद नकारात्मक पक्ष यह है, findकुछ भी करने के लिए मना कर दिया है, तो PATHकिसी भी संबंधित पथ शामिल है ... askubuntu.com/questions/621132/... find: The relative path XXX is included in the PATH environment
सिरो Santilli新疆改造中心法轮功六四事件

6

एक अन्य दृष्टिकोण आउटपुट while readपर एक लूप का उपयोग करना है find। यह प्रत्येक फ़ाइल नाम को एक चर के रूप में उपयोग करने की अनुमति देता है जिसे अतिरिक्त लागत / संभावित सुरक्षा मुद्दों के बारे में चिंता किए बिना हेरफेर किया जा सकता है जो एक अलग sh -cप्रक्रिया का उपयोग करने findके -execविकल्प का उपयोग कर रहा है ।

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

और यदि शेल का उपयोग किया जा रहा है, तो -dएक readसीमांकक को निर्दिष्ट करने के विकल्प का समर्थन करता है, तो आप निम्न नामित फ़ाइलों (जैसे एक नई पंक्ति के साथ) का समर्थन कर सकते हैं:

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

3

मैं पहले उत्तर पर विस्तार करना चाहता हूं और ध्यान दें कि यह फ़ाइल नाम को जोड़ने के लिए काम नहीं करेगा क्योंकि ./फ़ाइल नाम तर्क में उपसर्ग मौजूद है।

थॉमस एकर जवाब को संशोधित करते हुए, मुझे यह एक अधिक सामान्य दृष्टिकोण लगता है

find . -name PATTERN -printf "%f\0" | xargs --null -I{} mv {} "prefix {} suffix"

xargs विकल्प:

--nullइंगित करता है कि प्रत्येक तर्क stdinएक अशक्त चरित्र ( \0) के साथ समाप्त हो गया । इस तरह फ़ाइल नाम में स्थान हो सकते हैं, अन्यथा प्रत्येक शब्द को mvकमांड के लिए एक अलग पैरामीटर के रूप में धमकी दी जाएगी ।

-I replace-strreplace-strसे पढ़ा गया तर्क के साथ हर महासागरीयता को प्रतिस्थापित किया जाएगा stdin। तो, आप इसे अन्य स्ट्रिंग के लिए बदल सकते हैं यदि आपको इसकी आवश्यकता है।


pringf "%f\0"इसके बजाय एक क्यों print0?
SLM

1
@sim, क्योंकि उपसर्गों -print0का उत्पादन करेगा ./यदि PATTERNइसमें शेल मेटाचैकर शामिल हैं जो मूल नामों के उपसर्ग करने वाली किसी चीज़ का नाम बदलने पर रास्ते में मिलता है। (उदाहरण के लिए नाम बदलने 0 - foo.txtके लिए 00 - foo.txt, 1 - bar.txtकरने के लिए 01 - bar.txtआदि)
दास-जी

2

मैं के साथ इसी तरह कुछ करने के लिए कर रहा था for, findऔर mv

for i in $(find . -name 'config.yml'); do mv $i $i.bak; done

यह सभी config.ymlफाइलों को ढूंढता है और उनका नाम बदल देता हैconfig.yml.bak


इसका लाभ चर विस्तार का उपयोग करने में सक्षम होने के रूप में है। $ {मैं: r}। अच्छा जवाब।
सलामी

हाँ, आप बहुत अधिक अभिव्यक्ति कर सकते हैं forऔर एक बल्क ऑपरेशन कर सकते हैं।
वरुण रिसबड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.