नए bash प्रोग्रामर को बार-बार यह सलाह सुनने को मिलेगी:
अपने वेरिएबलो को उद्धृत कीजिए!
ऐसा क्यों?
जब आप किसी वेरिएबल (जैसे $name) को एक्सपैंड करते हैं, तो लगभग हमेशा आप चाहते हैं कि उस वेरिएबल की वैल्यू एक ही स्ट्रिंग (या संख्या) की तरह इस्तेमाल हो। आप नहीं चाहते कि वह वैल्यू अनजाने में दो या अधिक अलग-अलग स्ट्रिंग की तरह संभाली जाए।
किसी टेक्स्ट के टुकड़े को एक ही शब्द की तरह बरतने के लिए उद्धरण ही मुख्य तरीका है। उद्धरण चिह्न मुख्य रूप से दो तरह के होते हैं।
दोहरे उद्धरण चिह्नों के अंदर पैरामीटर एक्सपैंशन, अंकगणितीय एक्सपैंशन और कमांड सब्स्टिट्यूशन संभव हैं।
echo "This is bash version $BASH_VERSION"
किसी दोहरे उद्धरण चिह्नों वाली स्ट्रिंग के अंदर एक शाब्दिक डबल कोट डालने के लिए उससे पहले बैकस्लैश लगाइए।
echo "She said \"Let me teach you about bash.\""
एकल उद्धरण चिह्न अपने अंदर किसी भी तरह का एक्सपैंशन नहीं होने देते।
echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION
किसी एकल उद्धरण चिह्नों वाली स्ट्रिंग के अंदर एकल उद्धरण चिह्न डालना संभव नहीं है।
echo $name में गड़बड़ी क्या है?
जब शेल कोड की किसी लाइन को पार्स करता है, तो वह एक के बाद एक कई तरह के एक्सपैंशन करता है। इनमें से अंतिम दो एक्सपैंशन वर्ड स्प्लिटिंग और फाइलनेम एक्सपैंशन हैं।
पैरामीटर एक्सपैंशन, कमांड सब्स्टिट्यूशन या अंकगणितीय एक्सपैंशन का जो भी नतीजा दोहरे उद्धरण चिह्नों के अंदर नहीं बना, वह "वर्ड स्प्लिटिंग" के योग्य है। शेल एक्सपैंड हुए नतीजे को लेता है और उसे $IFS की वैल्यू में मौजूद अक्षरों के किसी भी क्रम पर तोड़ देता है। डिफ़ॉल्ट रूप से यह स्पेस, टैब और न्यूलाइन होता है।
इसे शेल लूप की मदद से काम करते हुए देख सकते हैं:
sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^
आउटपुट:
1 The quick brown fox jumps.
लेकिन बिना उद्धरण के:
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^
आउटपुट:
1 The
2 quick
3 brown
4 fox
5 jumps.
अब इसे $IFS की किसी और वैल्यू के साथ देखिए:
csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done
आउटपुट:
first
second
third
वर्ड स्प्लिटिंग बंद करने का एक तरीका यह है कि $IFS को खाली स्ट्रिंग पर सेट कर दिया जाए।
sentence="The quick brown fox jumps."
IFS= # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^
आउटपुट:
1 The quick brown fox jumps.
लेकिन वेरिएबलो को बिना उद्धरण के छोड़ना तब तक ठीक नहीं माना जाता, जब तक आप फाइलनेम एक्सपैंशन भी बंद न कर दें। ऐसा कैसे करें, यह हम नीचे देखेंगे।
"ग्लोब" पैटर्न फाइलों के किसी समूह से मेल खाने वाला छोटा और साफ़ पैटर्न लिखने के लिए इस्तेमाल होते हैं। जैसे, किसी डायरेक्टरी की सारी Markdown फाइलें *.md से मैच की जा सकती हैं। ग्लोब पैटर्न Unix के आविष्कार के समय से ही मौजूद हैं।
ग्लोब के वाइल्डकार्ड अक्षर ये हैं:
* किसी भी अक्षर के शून्य या अधिक होने से मेल खाता है,
? किसी भी अक्षर के ठीक एक होने से मेल खाता है,
[...] ब्रैकेट के अंदर दिए गए अक्षरों के समूह में से ठीक एक अक्षर से मेल खाता है।
[abc] ठीक एक ऐसे अक्षर से मेल खाता है जो a या b या c है।
^ के बाद)
[][] एक कैरेक्टर सेट है जो क्लोज़ या ओपन ब्रैकेट, दोनों में से किसी एक से मेल खाएगा।[[\]]
[x-y] ठीक एक ऐसे अक्षर से मेल खाता है जो x से y तक की रेंज में आता है
^ के बाद), या क्लोज़िंग ब्रैकेट से पहले अंतिम अक्षर।
[-xy] या [xy-] में से हर एक ठीक एक ऐसे अक्षर से मेल खाता है जो - या x या y है[x\-y]
[0-9A-Fa-f] किसी हेक्साडेसिमल अंक से मेल खाता है[^abc] ठीक एक ऐसे अक्षर से मेल खाता है जो a या b या c नहीं है,[^x-y] ठीक एक ऐसे अक्षर से मेल खाता है जो x से y तक की रेंज में नहीं आता[[:character_class:]] नामित "character_class" में आने वाले एक अक्षर से मेल खाता है
lower -- छोटे अक्षरupper -- बड़े अक्षरalpha -- अक्षरdigit -- अंकalnum -- अक्षर और अंकspace -- व्हाइटस्पेसblank -- आड़ा व्हाइटस्पेस (स्पेस और टैब)xdigit -- हेक्साडेसिमल अंकword -- किसी आइडेंटिफ़ायर में इस्तेमाल हो सकने वाले अक्षर (अक्षर, संख्याएँ, अंडरस्कोर)punct -- विराम चिह्नcntrl -- कंट्रोल अक्षरgraph -- "दिखने वाले" अक्षरprint -- दिखने वाले अक्षर और स्पेस[[:alpha:]] क्लास [[:lower:][:upper:]] से बनती है[[:alnum:]] क्लास [[:alpha:][:digit:]] से बनती है[[:word:]] क्लास [[:alnum:]_] से बनती है[[:word:]] और [[:punct:]] दोनों है[[:graph:]] क्लास [[:alnum:][:punct:]] से बनती है[[:print:]] क्लास [[:graph:] ] से बनती है -- सिर्फ़ स्पेस (ऑक्टल 040), कोई और व्हाइटस्पेस नहीं।आप for लूप की मदद से फाइलों की सूची पर इटरेशन कर सकते हैं
for file in *.csv; do
do_something_with "$file"
done
ध्यान दीजिए कि "$file" वेरिएबल उद्धृत है। ग्लोब पैटर्न ऐसी फाइलें भी लौटा सकता है जिनके नाम में स्पेस हो। हमें वेरिएबल को उद्धृत करना होगा ताकि do_something_with कमांड को ठीक एक आर्गुमेंट मिले, वह फाइल का नाम।
ग्लोब पैटर्न रेगुलर एक्सप्रेशन नहीं हैं।
.
?
.*
*
.* का मतलब है "ऐसी फाइल के नाम से मेल खाना जिसका पहला अक्षर शाब्दिक डॉट हो और उसके बाद शून्य या अधिक कोई भी अक्षर हों"।.+
?*
Bash रेगुलर एक्सप्रेशन के और करीब एक्सटेंडेड पैटर्न देता है।
कभी-कभी आप फाइलनेम एक्सपैंशन रोकना चाहते हैं। यूज़र का इनपुट संभालना ऐसा ही एक मौका है। इस कोड स्निपेट को देखिए
read -p "Enter something: " user_input
echo $user_input
अगर यूज़र एक एस्टरिस्क (*) डालता है, तो आउटपुट क्या होगा? मौजूदा डायरेक्टरी की फाइलों की सूची। शायद आप यही नहीं करना चाहते।
एक और उदाहरण है: ऐसे प्रोग्राम को आर्गुमेंट के तौर पर पैटर्न देना जो पैटर्न की उम्मीद करते हैं। tr प्रोग्राम अकसर टेक्स्ट में बदलाव करने के लिए इस्तेमाल होता है। किसी वेरिएबल की वैल्यू को छोटे अक्षरों में बदलने के लिए आप यह कर सकते हैं
var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'
आउटपुट:
my puppet
एक आम गलती है tr के आर्गुमेंट उद्धृत करना भूल जाना: tr [:upper:] [:lower:]
यह समस्या क्यों है?
बिना उद्धरण वाले ग्लोब पैटर्न मेल खाने वाली फाइलों की सूची में बदल दिए जाएँगे।
मान लीजिए आपकी स्क्रिप्ट इस्तेमाल करने वाले व्यक्ति की मौजूदा डायरेक्टरी में p, u और w नाम की फाइलें हैं:
touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]
इससे एक एरर मिलती है!
बिना उद्धरण वाला [:upper:] पैटर्न :, u, p, e या r में से किसी से भी मेल खाता है।
बिना उद्धरण वाला [:lower:] पैटर्न :, l, o, w, e या r में से किसी से भी मेल खाता है।
Bash पहले पैटर्न के लिए p और u फाइलों से मेल करता है, दूसरे पैटर्न के लिए w फाइल से, और tr को ऐसे कॉल किया जाता है
echo "$var" | tr p u w
और tr के लिए यह आर्गुमेंट की गलत संख्या है।
फाइलनेम एक्सपैंशन बंद करने के लिए आप set कमांड इस्तेमाल कर सकते हैं।
echo * # prints a list of files
set -f # disable filename expansion
echo * # prints a literal asterisk
set +f # enable filename expansion
बिल्टिन shopt कमांड की कई सेटिंग्स होती हैं जो तय करती हैं कि फाइलनेम एक्सपैंशन कैसे काम करे। इनमें से दो दिलचस्प सेटिंग्स ये हैं:
shopt -s nocaseglob -- केस की परवाह किए बिना मैच करनाshopt -s extglob -- एक्सटेंडेड पैटर्न चालू करनाshopt -s nullglob -- अगर कोई फाइल मेल न खाए, तो पैटर्न की जगह कुछ न रखें।
डिफ़ॉल्ट व्यवहार यह है कि पैटर्न को शाब्दिक स्ट्रिंग की तरह वैसे ही छोड़ दिया जाता है।