रिक्त स्थान (या वर्कअराउंड) के साथ BASH स्क्रिप्ट को 'फॉर' हैंडल फ़ाइलनाम बनाना


12

जब भी मैं कई वर्षों से BASH का उपयोग कर रहा हूं, BASH स्क्रिप्टिंग के साथ मेरा अनुभव अपेक्षाकृत सीमित है।

मेरा कोड नीचे है। यह वर्तमान निर्देशिका के भीतर से पूरी निर्देशिका संरचना को पकड़ना चाहिए और इसे फिर से भरना चाहिए $OUTDIR

for DIR in `find . -type d -printf "\"%P\"\040"`
do
  echo mkdir -p \"${OUTPATH}${DIR}\"        # Using echo for debug; working script will simply execute mkdir
  echo Created $DIR
done

समस्या यह है, यहाँ मेरी फ़ाइल संरचना का एक नमूना है:

$ ls
Expect The Impossible-Stellar Kart
Five Iron Frenzy - Cheeses...
Five Score and Seven Years Ago-Relient K
Hello-After Edmund
I Will Go-Starfield
Learning to Breathe-Switchfoot
MMHMM-Relient K

रिक्त स्थान पर ध्यान दें: -S और forशब्द द्वारा पैरामीटर शब्द लेता है, इसलिए मेरी स्क्रिप्ट का आउटपुट कुछ इस तरह दिखता है:

Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Learning"
Created Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot"
Created Breathe-Switchfoot

लेकिन मुझे इसके आउटपुट से पूरे फाइलनाम (एक समय में एक लाइन) को हथियाने की आवश्यकता है find। मैंने findप्रत्येक फ़ाइलनाम के आसपास डबल-कोट्स बनाने की भी कोशिश की है । लेकिन यह मदद नहीं करता है।

for DIR in `find . -type d -printf "\"%P\"\040"`

और इस परिवर्तित लाइन के साथ आउटपुट:

Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"""
Created ""
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"Learning"
Created "Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot""
Created Breathe-Switchfoot"

अब, मुझे कुछ इस तरह की आवश्यकता है कि मैं इस तरह के माध्यम से पुनरावृत्ति कर सकता हूं, क्योंकि मैं gstreamerएक समान संरचना में प्रत्येक फ़ाइल पर एक और अधिक जटिल कमांड चलाने की इच्छा रखता हूं । मुझे यह कैसे करना चाहिए?

संपादित करें: मुझे एक कोड संरचना की आवश्यकता है जो मुझे प्रत्येक निर्देशिका / फ़ाइल / लूप के लिए कोड की कई लाइनें चलाने की अनुमति देगा। क्षमा करें यदि मैं अस्पष्ट था।

समाधान: मैंने शुरू में कोशिश की:

find . -type d | while read DIR
do
  mkdir -p "${OUTPATH}${DIR}"
  echo Created $DIR
done

इसने अधिकांश भाग के लिए ठीक काम किया। हालाँकि, मैंने बाद में पाया कि जब सब-ओवर में चलने वाले लूप में पाइप का परिणाम होता है, तो लूप में सेट किए गए कोई भी चर बाद में अनुपलब्ध थे जो एक त्रुटि काउंटर को लागू करना काफी मुश्किल बना। मेरा अंतिम समाधान ( SO पर इस उत्तर से ):

while read DIR
do
  mkdir -p "${OUTPATH}${DIR}"
  echo Created $DIR
done < <(find . -type d)

इसने मुझे बाद में लूप के भीतर सशर्त रूप से वृद्धि चर की अनुमति दी जो बाद में स्क्रिप्ट में उपलब्ध रहेगी।


Why_would_you_ever_need_a_space_in_a_file_name?
केविन पेंको

सच है, मेरी प्राथमिकता नहीं। हालाँकि, रिक्त स्थान को निकालने के लिए, आपको पहले रिक्त स्थान वाली फ़ाइलों को संभालना होगा;)
सैमुअल जैशके

1
वास्तव में, फ़ाइल नामों को रिक्त स्थान की अनुमति देनी चाहिए। मैं कुछ भी /और लेकिन अनपेक्षित वर्णों की अनुमति देता हूं । लेकिन कुछ भी अनुमति दी जाती है /और \0इसलिए आपको उन्हें अनुमति देनी चाहिए।
केविन पैंको

जवाबों:


11

आपको findएक whileलूप में पाइप करने की आवश्यकता है ।

find ... | while read -r dir
do
    something with "$dir"
done

साथ ही, आपको -printfइस मामले में उपयोग करने की आवश्यकता नहीं होगी ।

आप उनके नामों के साथ फ़ाइलों के खिलाफ यह सबूत बना सकते हैं, यदि आप चाहें, तो एक अशक्त सीमांकक का उपयोग करके (जो कि एकमात्र चरित्र है जो * nix filepath में प्रकट नहीं हो सकता):

find ... -print0 | while read -d '' -r dir
do
    something with "$dir"
done

तुम भी $()backticks के बजाय अधिक बहुमुखी और आसान होने का उपयोग कर पाएंगे । उन्हें और अधिक आसानी से नेस्टेड किया जा सकता है और उद्धृत करना अधिक आसानी से किया जा सकता है। यह आकस्मिक उदाहरण इन बिंदुओं को चित्रित करेगा:

echo "$(echo "$(echo "hello")")"

बैकटिक्स के साथ ऐसा करने की कोशिश करें।


2
इसके अलावा, इसके बजाय "$dir", यह उपयोग करने के लिए बेहतर है "${dir}"- $ {dir} नाम और $ {dirname} के बीच अंतर बताना आसान है, लेकिन $ dirname को किसी भी तरह से व्याख्या किया जा सकता है।
जेम्स पोली

यहाँ महत्वपूर्ण बात यह है कि इसमें readएक पूरी पंक्ति पढ़ी जाती है ${dir}, इसलिए IFS कोई मायने नहीं रखता।
जेम्स पोली

1
$ / "टाइपो खोजने के लिए धन्यवाद। यदि चर नाम के बाद कुछ भी नहीं है तो ब्रेस आवश्यक नहीं हैं।
अगली सूचना तक रोका गया।

4
यह रिक्त स्थान (U + 0020) के साथ पथनामों को संभाल लेगा, लेकिन यह अभी भी लाइन फीड्स (U + 000A) के साथ ठीक से पथनामों को संभालने में विफल रहेगा। मैं पसंद find … -print0 | xargs -0 …करता हूं क्योंकि इसका उपयोग करने वाला सीमांकक केवल उसी पात्र से मेल खाता है जिसे पोसिक्स पथानमों में अनुमति नहीं है: NUL (U + 0000)।
क्रिस जॉन्सन

2
उत्तम! मुझे इसकी ही खोज थी। यह मेरे लिए कभी नहीं हुआ था कि आप पाइप करने में सक्षम हो सकें while। @ क्रिस जॉन्सन: यह सच है, लेकिन यहां तक ​​कि संगीत तेजस्वी कार्यक्रम उनके फ़ाइलनाम में लाइनफ़ीड्स डालना नहीं चाहते हैं। और अगर वे करते हैं, तो मैं जानना चाहता हूं (अर्थात: कुछ गलत हो जाता है) और तुरंत उनसे छुटकारा
पाएं

8

यह उत्तर देखें मैंने कुछ दिनों पहले एक स्क्रिप्ट के उदाहरण के लिए लिखा था जो रिक्त स्थान के साथ फ़ाइलनाम को संभालती है।

हालांकि आप जो करने की कोशिश कर रहे हैं उसे प्राप्त करने के लिए थोड़ा और अधिक जटिल (लेकिन अधिक संक्षिप्त) तरीका है:

find . -type d -print0 | xargs -0 -I {} mkdir -p ../theredir/{}

-print0बताता है कि एक अशक्त के साथ तर्कों को अलग करना; -0 से xargs इसे nulls द्वारा किए गए तर्कों की अपेक्षा करता है। इसका मतलब है कि यह रिक्त स्थान को संभालता है।

-I {}{}फ़ाइल नाम के साथ स्ट्रिंग को बदलने के लिए xargs बताता है । इसका मतलब यह भी है कि प्रति कमांडलाइन में केवल एक फ़ाइल नाम का उपयोग किया जाना चाहिए (xargs सामान्य रूप से सामान होगा जो लाइन पर फिट होगा)

बाकी सब स्पष्ट होना चाहिए।


डेनिस विलियमसन का सुझाव हालांकि, (टाइपोस से हटकर) अधिक पठनीय है, और इस तरह लगभग हर तरह से बेहतर है।
जेम्स पोली

काम करता है, mkdir के लिए, लेकिन माफ करना मुझे अधिक स्पष्ट होना चाहिए था - मैं प्रत्येक फ़ाइल के लिए कमांड की एक श्रृंखला चलाने की इच्छा रखता हूं। आप देखते हैं, मेरी इसी तरह की दिनचर्या के लिए बाद में मैं इनपुट फाइल के आधार पर एक आउटपुट फ़ाइल नाम बनाना चाहता हूं (जिसमें .ogg एक्सटेंशन और .mp3 को अलग करना शामिल है) और फिर gst-launch को लागू करते समय अपने पाइपलाइन में इन कई चर का उपयोग करें।
शमूएल जैशेके

5

समस्या जो आप सामना कर रहे हैं वह यह है कि विवरण के लिए अलग-अलग तर्क के रूप में जवाब दिया जा रहा है। अंतरिक्ष परिसीमन। आपको अंतरिक्ष पर विभाजित न होने के लिए बैश के IFS चर का उपयोग करने की आवश्यकता है।

यहां एक लिंक दिया गया है जो बताता है कि यह कैसे करना है।

IFS आंतरिक चर

इस समस्या का एक तरीका बैश के आंतरिक IFS (आंतरिक क्षेत्र विभाजक) चर को बदलना है, ताकि यह इस मामले में, डिफ़ॉल्ट व्हाट्सएप (स्पेस, टैब, न्यूलाइन) के अलावा कुछ और क्षेत्रों को विभाजित करे।

#!/bin/bash
IFS=$';'

for I in `find -type d -printf \"%P\"\;`
do
   echo "== $I =="
done

% P के बाद अपने फ़ील्ड सीमांकक को आउटपुट करने के लिए अपनी खोज सेट करें और अपने IFS को उचित रूप से सेट करें। मैंने अर्ध-बृहदान्त्र को चुना क्योंकि यह आपके फ़ाइलनाम में बहुत संभावना नहीं है।

अन्य विकल्प यह है कि आप सीधे -execलूप के लिए स्किप कर सकते हैं के माध्यम से mkdir कॉल करें । अगर आपको कोई अतिरिक्त पार्स करने की जरूरत नहीं है।


क्या होगा यदि फ़ाइल नाम में IFS शामिल है? फिर आपको एक अलग चुनना होगा। लेकिन फिर, क्या हुआ अगर ...
अगली सूचना तक रोक दिया गया।

3
आप /POSIX, और :DOS फाइल सिस्टम पर चुन सकते हैं । विभिन्न फाइल सिस्टम के लिए अवैध वर्ण हैं जिन्हें आप IFS के लिए चुन सकते हैं। अधिक जटिल कुछ भी और आप बेहतर पर्ल का उपयोग कर रहे हैं।
डैरेन हॉल

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

वह भी काफी उपयोगी लगता है। मैं whileविकल्प के लिए पाइप के साथ गया हूं , लेकिन यह भी काफी व्यावहारिक है। हां, अपनी इसी संरचना में बाद में मुझे और अधिक पार्स करने की जरूरत थी। (इनपुट फ़ाइल नाम जो के रूप में पारित किया जाएगा .ogg होगा, filesrcजीएसटी पाइप लाइन में है, लेकिन .mp3 में एक बराबर समाप्त होने के उत्पादन निर्देशिका में आधारित उत्पन्न और इसमें एक के रूप में भी पाइप लाइन के लिए पारित किया filesinkहै, और इस पाठ्यक्रम की जरूरतों के लिए किया जाना प्रत्येक फ़ाइल के echoलिए, उपयोगकर्ता के साथ कुछ ।)
सैमुअल जैशके

4

यदि आपके लूप का शरीर एकल कमांड से अधिक है, तो शेल स्क्रिप्ट का उपयोग करने के लिए xargs का उपयोग करना संभव है :

export OUTPATH=/some/where/else/
find . -type d -print0 | xargs -0 bash -c 'for DIR in "$@"; do
  printf "mkdir -p %q\\n" "${OUTPATH}${DIR}"        # Using echo for debug; working script will simply execute mkdir
  echo Created $DIR
done' -

यदि शेल बॉर्न / POSIX किस्म का हो (इसका उपयोग शेल स्क्रिप्ट में $ 0 सेट करने के लिए किया जाता है) तो ट्रेलिंग डैश (या कुछ अन्य 'शब्द') को शामिल करना न भूलें। इसके अलावा, उद्धरण के साथ देखभाल की जानी चाहिए, क्योंकि शेल स्क्रिप्ट को प्रांप्ट पर सीधे के बजाय एक उद्धृत स्ट्रिंग के अंदर लिखा जा रहा है।


एक और दिलचस्प अवधारणा। धन्यवाद - मुझे यकीन है कि मुझे बाद में इसके लिए एक उपयोग मिल जाएगा :)
शमूएल जैशेके

1

आपके अपडेट किए गए प्रश्न में

mkdir -p \"${OUTPATH}${DIR}\"

ये होना चाहिए

mkdir -p "${OUTPATH}${DIR}"

धन्यवाद। फिक्स्ड। यह DIR के बजाय FILENAME को भी पढ़ रहा था - कॉपी-पेस्ट: P
सैमुअल जैस्चके


0

या पूरी चीज को बहुत कम जटिल बनाने के लिए:

% rsync -av --include='*/' --exclude='*' SRC DST

यह SRC की निर्देशिका संरचना को DST में दोहराता है।


नहीं, मुझे इसकी तरह एक पुनरावृत्त संरचना की आवश्यकता है, जो मुझे प्रत्येक फ़ाइल के लिए कोड की कई लाइनें चलाने की अनुमति देता है। "अब, मुझे कुछ इस तरह की आवश्यकता है कि मैं इस तरह के माध्यम से पुनरावृत्ति कर सकता हूं, क्योंकि मैं एक समान संरचना में प्रत्येक फ़ाइल पर gstreamer से अधिक जटिल कमांड चलाने की इच्छा रखता हूं।" क्षमा करें यदि मैं अस्पष्ट था।
सैमुअल जैस्चके

आपके द्वारा मांगी गई समस्या का हल मैं करता हूं, इससे कोई फर्क नहीं पड़ता कि यह आपकी तरफ से एक बड़ी 'पाइपलाइन' का हिस्सा है। किसी और के लिए समस्या होने के रूप में प्रश्न में वर्णित rsync-दृष्टिकोण काम करेगा। इसलिए, संभावित अस्पष्टता के बारे में खेद करने की आवश्यकता नहीं है :)
अकीरा

हाँ। नहीं, मेरा मतलब है कि मैं इसी तरह का उपयोग कर रहा हूं whiledo... doneसंरचना बाद में इसी तरह के प्रसंस्करण को खोजने से करने के लिए, जिसके लिए प्रत्येक फ़ाइल पर कोड की कई लाइनें चलाने की आवश्यकता होगी (स्ट्रिंग, इको, जीएसटी-लॉन्च, आदि को संशोधित करें)। ) और rsyncयह हासिल नहीं करेगा। इसीलिए मैंने निर्दिष्ट किया कि मुझे समान समरूपता के भीतर अधिक जटिल सेटों को चलाने में सक्षम होना चाहिए। मेरी स्क्रिप्ट दो बार इस लूप संरचना का उपयोग करती है, इसलिए इस प्रश्न के लिए मैंने बीच में कम क्रूड के साथ पोस्ट किया।
शमूएल जैशेके

0

यदि आपके पास GNU समानांतर http: // www.gnu.org/software/parallel/ स्थापित है, तो आप ऐसा कर सकते हैं:

find . -type d | parallel echo making {} ";" mkdir -p /tmp/outdir/{} ";" echo made {}

अधिक जानने के लिए GNU समानांतर के इंट्रो वीडियो देखें: http://www.youtube.com/watch?v=OpaiGYxkSuQ

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