बैश एक शक्तिशाली प्रोग्रामिंग भाषा है, जो कमांड लाइन और शेल स्क्रिप्ट में उपयोग के लिए पूरी तरह से डिज़ाइन की गई है। यह तीन-भागों की श्रृंखला, मेरे तीन-खंड वाले लिनक्स सेल्फ-स्टडी कोर्स पर आधारित, कमांड-लाइन इंटरफेस (CLI) पर बैश को प्रोग्रामिंग भाषा के रूप में उपयोग करने का पता लगाती है।
इस श्रृंखला के पहले लेख में बैश के साथ कुछ सरल कमांड-लाइन प्रोग्रामिंग का पता लगाया गया, जिसमें वेरिएबल्स और कंट्रोल ऑपरेटर्स का उपयोग शामिल था। दूसरे लेख में उन फाइल, स्ट्रिंग, न्यूमेरिक और विविध लॉजिकल ऑपरेटर्स की जांच की गई जो एक्जीक्यूशन-फ्लो कंट्रोल लॉजिक और बैश में विभिन्न प्रकार के शेल एक्सपेंशन प्रदान करते हैं। यह तीसरा (और अंतिम) लेख पुनरावृत्त संचालन करने के लिए लूप्स के उपयोग और उन लूप्स को नियंत्रित करने के तरीकों की जांच करता है।
लूप्स
मैंने जितनी भी प्रोग्रामिंग भाषाओं का उपयोग किया है, उन सभी में कम से कम कुछ प्रकार के लूप स्ट्रक्चर होते हैं जो दोहराव वाले संचालन करने की विभिन्न क्षमताएं प्रदान करते हैं। मैं फॉर लूप का काफी बार उपयोग करता हूं लेकिन मुझे व्हाइल और अनटिल लूप भी उपयोगी लगते हैं।
फॉर लूप्स
मेरी राय में, for कमांड का बैश कार्यान्वयन अधिकांश की तुलना में थोड़ा अधिक लचीला है क्योंकि यह गैर-संख्यात्मक मानों को संभाल सकता है; इसके विपरीत, उदाहरण के लिए, मानक C भाषा का for लूप केवल संख्यात्मक मानों को डील कर सकता है।
for कमांड के बैश संस्करण की मूल संरचना सरल है:
for Var in list1 ; do list2 ; done
इसका अनुवाद है: "list1 में प्रत्येक मान के लिए, $Var को उस मान पर सेट करें और फिर उस मान का उपयोग करके list2 में प्रोग्राम स्टेटमेंट्स को निष्पादित करें; जब list1 के सभी मानों का उपयोग हो चुका हो, तो यह समाप्त हो जाता है, इसलिए लूप से बाहर निकलें।" list1 में मान मूल्यों की एक सरल, स्पष्ट स्ट्रिंग हो सकते हैं, या वे कमांड सब्स्टीट्यूशन का परिणाम हो सकते हैं (श्रृंखला के दूसरे लेख में वर्णित)। मैं इस निर्माण का अक्सर उपयोग करता हूं।
इसे आजमाने के लिए, सुनिश्चित करें कि ~/testdir अभी भी वर्तमान कार्य निर्देशिका (PWD) है। निर्देशिका को साफ करें, फिर मानों की स्पष्ट सूची के साथ शुरू होने वाले for लूप के एक तुच्छ उदाहरण को देखें। यह सूची अल्फ़ान्यूमेरिक मानों का मिश्रण है - लेकिन यह न भूलें कि सभी वेरिएबल्स स्ट्रिंग्स हैं और उन्हें इस तरह माना जा सकता है।
[student@studentvm1 testdir]$ rm *
[student@studentvm1 testdir]$ for I in a b c d 1 2 3 4 ; do echo $I ; done
a
b
c
d
1
2
3
4
यहां एक अधिक सार्थक वेरिएबल नाम के साथ थोड़ा अधिक उपयोगी संस्करण है:
[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Department $Dept" ; done
Department Human Resources
Department Sales
Department Finance
Department Information Technology
Department Engineering
Department Administration
Department Research
कुछ निर्देशिकाएं बनाएं (और ऐसा करते समय कुछ प्रगति जानकारी दिखाएं):
[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept" ; done
Working on Department Human Resources
Working on Department Sales
Working on Department Finance
Working on Department Information Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Administration
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Engineering
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Finance
drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Human Resources'
drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Information Technology'
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Research
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Sales
$Dept वेरिएबल को mkdir स्टेटमेंट में कोट्स में संलग्न किया जाना चाहिए; अन्यथा, दो-भाग वाले विभाग नाम (जैसे "Information Technology") को दो अलग-अलग विभागों के रूप में माना जाएगा। यह एक सर्वोत्तम अभ्यास पर प्रकाश डालता है जिसका मैं पालन करना पसंद करता हूं: सभी फाइल और निर्देशिका नाम एक ही शब्द होने चाहिए। हालांकि अधिकांश आधुनिक ऑपरेटिंग सिस्टम नामों में स्पेस को डील कर सकते हैं, लेकिन सिसएडमिन के लिए यह सुनिश्चित करने के लिए अतिरिक्त काम करना पड़ता है कि उन विशेष मामलों पर स्क्रिप्ट और CLI प्रोग्राम में विचार किया जाए। (उन पर लगभग निश्चित रूप से विचार किया जाना चाहिए, भले ही वे कष्टप्रद हों क्योंकि आप कभी नहीं जानते कि आपके पास कौन सी फाइलें होंगी।)
तो, ~/testdir में सब कुछ हटाएं - फिर से - और इसे एक बार और करें:
[student@studentvm1 testdir]$ rm -rf * ; ll
total 0
[student@studentvm1 testdir]$ for Dept in Human-Resources Sales Finance Information-Technology Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept" ; done
Working on Department Human-Resources
Working on Department Sales
Working on Department Finance
Working on Department Information-Technology
Working on Department Engineering
Working on Department Administration
Working on Department Research
[student@studentvm1 testdir]$ ll
total 28
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Administration
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Engineering
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Finance
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Human-Resources
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Information-Technology
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Research
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Sales
मान लीजिए कोई किसी विशेष लिनक्स कंप्यूटर पर सभी RPMs की सूची और प्रत्येक का संक्षिप्त विवरण मांगता है। जब मैं नॉर्थ कैरोलिना राज्य के लिए काम करता था तो यह मेरे साथ हुआ। चूंकि उस समय ओपन सोर्स राज्य एजेंसियों द्वारा उपयोग के लिए "अनुमोदित" नहीं था, और मैं केवल अपने डेस्कटॉप कंप्यूटर पर लिनक्स का उपयोग करता था, पॉइंटी-हेयर्ड बॉस (PHBs) को मेरे कंप्यूटर पर इंस्टॉल प्रत्येक सॉफ्टवेयर की सूची चाहिए थी ताकि वे एक "अपवाद" को "अनुमोदित" कर सकें।
आप उस समस्या को कैसे हल करेंगे? यहां एक तरीका है, यह जानकर शुरू करें कि rpm –qa कमांड एक RPM का पूरा विवरण प्रदान करता है, जिसमें वे दो आइटम शामिल हैं जो PHBs चाहते हैं: सॉफ्टवेयर नाम और एक संक्षिप्त सारांश।
अंतिम परिणाम तक एक बार में एक कदम बढ़ाएं। पहले, सभी RPMs सूचीबद्ध करें:
[student@studentvm1 testdir]$ rpm -qa
perl-HTTP-Message-6.18-3.fc29.noarch
perl-IO-1.39-427.fc29.x86_64
perl-Math-Complex-1.59-429.fc29.noarch
lua-5.3.5-2.fc29.x86_64
java-11-openjdk-headless-11.0.ea.28-2.fc29.x86_64
util-linux-2.32.1-1.fc29.x86_64
libreport-fedora-2.9.7-1.fc29.x86_64
rpcbind-1.2.5-0.fc29.x86_64
libsss_sudo-2.0.0-5.fc29.x86_64
libfontenc-1.1.3-9.fc29.x86_64
<snip>
सूची को क्रमबद्ध करने और अद्वितीय प्रिंट करने के लिए sort और uniq कमांड जोड़ें (क्योंकि समान नाम वाले कुछ RPMs इंस्टॉल होना संभव है):
[student@studentvm1 testdir]$ rpm -qa | sort | uniq
a2ps-4.14-39.fc29.x86_64
aajohan-comfortaa-fonts-3.001-3.fc29.noarch
abattis-cantarell-fonts-0.111-1.fc29.noarch
abiword-3.0.2-13.fc29.x86_64
abrt-2.11.0-1.fc29.x86_64
abrt-addon-ccpp-2.11.0-1.fc29.x86_64
abrt-addon-coredump-helper-2.11.0-1.fc29.x86_64
abrt-addon-kerneloops-2.11.0-1.fc29.x86_64
abrt-addon-pstoreoops-2.11.0-1.fc29.x86_64
abrt-addon-vmcore-2.11.0-1.fc29.x86_64
<snip>
चूंकि यह उन RPMs की सही सूची देता है जिन्हें आप देखना चाहते हैं, आप इसका उपयोग लूप के लिए इनपुट सूची के रूप में कर सकते हैं जो प्रत्येक RPM का सारा विवरण प्रिंट करेगा:
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done
यह कोड आपके इच्छित डेटा से कहीं अधिक डेटा उत्पन्न करता है। ध्यान दें कि लूप पूरा हो चुका है। अगला कदम केवल वही जानकारी निकालना है जो PHBs ने अनुरोध की थी। तो, एक egrep कमांड जोड़ें, जिसका उपयोग ^Name या ^Summary को चुनने के लिए किया जाता है। कैरेट (^) लाइन की शुरुआत को निर्दिष्ट करता है; इस प्रकार, लाइन की शुरुआत में Name या Summary वाली किसी भी लाइन को प्रदर्शित किया जाता है।
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary"
Name : a2ps
Summary : Converts text and other types of files to PostScript
Name : aajohan-comfortaa-fonts
Summary : Modern style true type font
Name : abattis-cantarell-fonts
Summary : Humanist sans serif font
Name : abiword
Summary : Word processing program
Name : abrt
Summary : Automatic bug detection and reporting tool
<snip>
आप उपरोक्त कमांड में egrep के बजाय grep आज़मा सकते हैं, लेकिन यह काम नहीं करेगा। आप इस कमांड के आउटपुट को less फ़िल्टर के माध्यम से पाइप करके परिणामों का पता लगा सकते हैं। अंतिम कमांड अनुक्रम इस तरह दिखता है:
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary" > RPM-summary.txt
यह कमांड-लाइन प्रोग्राम पाइपलाइन्स, रीडायरेक्शन, और एक for लूप का उपयोग करता है - वह भी एक ही लाइन पर। यह आपके छोटे CLI प्रोग्राम के आउटपुट को एक फाइल में रीडायरेक्ट करता है जिसे ईमेल में या अन्य उद्देश्यों के लिए इनपुट के रूप में उपयोग किया जा सकता है।
प्रोग्राम को एक बार में एक कदम बनाने की यह प्रक्रिया आपको प्रत्येक चरण के परिणाम देखने और यह सुनिश्चित करने की अनुमति देती है कि यह आपकी अपेक्षा के अनुसार काम कर रहा है और वांछित परिणाम प्रदान करता है।
इस अभ्यास से, PHBs को 1,900 से अधिक अलग-अलग RPM पैकेजों की सूची प्राप्त हुई। मुझे गंभीरता से संदेह है कि किसी ने उस सूची को पढ़ा होगा। लेकिन मैंने उन्हें ठीक वही दिया जो उन्होंने मांगा था, और मुझे इसके बारे में फिर कभी उनसे कुछ नहीं सुनने को मिला।
अन्य लूप्स
बैश में दो और प्रकार के लूप स्ट्रक्चर उपलब्ध हैं: while और until स्ट्रक्चर, जो सिंटैक्स और फ़ंक्शन दोनों में एक-दूसरे के बहुत समान हैं। इन लूप स्ट्रक्चर का मूल सिंटैक्स सरल है:
while [ expression ] ; do list ; done
और
until [ expression ] ; do list ; done
पहले का तर्क पढ़ता है: "जब तक एक्सप्रेशन ट्रू के रूप में मूल्यांकन करता है, प्रोग्राम स्टेटमेंट्स की सूची निष्पादित करें। जब एक्सप्रेशन फॉल्स के रूप में मूल्यांकन करता है, तो लूप से बाहर निकलें।" और दूसरा: "जब तक एक्सप्रेशन ट्रू के रूप में मूल्यांकन नहीं करता, प्रोग्राम स्टेटमेंट्स की सूची निष्पादित करें। जब एक्सप्रेशन ट्रू के रूप में मूल्यांकन करता है, तो लूप से बाहर निकलें।"
व्हाइल लूप
while लूप का उपयोग प्रोग्राम स्टेटमेंट्स की एक श्रृंखला को तब तक निष्पादित करने के लिए किया जाता है जब तक (यानी जब तक) लॉजिकल एक्सप्रेशन ट्रू के रूप में मूल्यांकन करता है। आपका PWD अभी भी ~/testdir होना चाहिए।
while लूप का सबसे सरल रूप वह है जो हमेशा के लिए चलता है। निम्न रूप सत्य कथन का उपयोग करके हमेशा एक "सत्य" रिटर्न कोड उत्पन्न करता है। आप एक साधारण "1" का भी उपयोग कर सकते हैं - और यह बिल्कुल उसी तरह काम करेगा - लेकिन यह सत्य स्टेटमेंट के उपयोग को दर्शाता है:
[student@studentvm1 testdir]$ X=0 ; while [ true ] ; do echo $X ; X=$((X+1)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 testdir]$
यह CLI प्रोग्राम अब अधिक समझ में आना चाहिए क्योंकि आपने इसके भागों का अध्ययन किया है। पहले, यह $X को शून्य पर सेट करता है ताकि यदि पिछले प्रोग्राम या CLI कमांड से इसका कोई मान बचा हो। फिर, चूंकि लॉजिकल एक्सप्रेशन [ true ] हमेशा 1 के रूप में मूल्यांकन करता है, जो सत्य है, do और done के बीच प्रोग्राम निर्देशों की सूची हमेशा के लिए निष्पादित होती है - या जब तक आप Ctrl+C दबाते हैं या अन्यथा प्रोग्राम को सिग्नल 2 भेजते हैं। वे निर्देश एक अंकगणितीय एक्सपेंशन हैं जो $X का वर्तमान मान प्रिंट करते हैं और फिर इसे एक से बढ़ाते हैं।
The Linux Philosophy for Sysadmins के सिद्धांतों में से एक है सुंदरता के लिए प्रयास करना, और सुंदरता प्राप्त करने का एक तरीका सादगी है। आप इस प्रोग्राम को वेरिएबल इन्क्रीमेंट ऑपरेटर, ++ का उपयोग करके सरल बना सकते हैं। पहले उदाहरण में, वेरिएबल का वर्तमान मान प्रिंट होता है, और फिर वेरिएबल बढ़ाया जाता है। यह वेरिएबल के बाद ++ ऑपरेटर रखकर दर्शाया जाता है:
[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9
अब प्रोग्राम के अंत से | head हटाएं और इसे फिर से चलाएं।
इस संस्करण में, वेरिएबल का मान प्रिंट होने से पहले बढ़ाया जाता है। यह वेरिएबल से पहले ++ ऑपरेटर रखकर निर्दिष्ट किया जाता है। क्या आप अंतर देख सकते हैं?
[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((++X)) ; done | head
1
2
3
4
5
6
7
8
9
आपने दो स्टेटमेंट्स को एक में कम कर दिया है जो वेरिएबल का मान प्रिंट करता है और उस मान को बढ़ाता है। एक डिक्रीमेंट ऑपरेटर, -- भी है।
आपको एक विशिष्ट संख्या पर लूप को रोकने की विधि चाहिए। इसे पूरा करने के लिए, सत्य एक्सप्रेशन को एक वास्तविक संख्यात्मक मूल्यांकन एक्सप्रेशन में बदलें। प्रोग्राम को 5 तक लूप करने और रोकने दें। नीचे दिए गए उदाहरण कोड में, आप देख सकते हैं कि -le "कम या बराबर" के लिए संख्यात्मक लॉजिकल ऑपरेटर है। इसका अर्थ है: "जब तक $X 5 से कम या बराबर है, लूप जारी रहेगा। जब $X बढ़कर 6 हो जाता है, तो लूप समाप्त होता है।"
[student@studentvm1 ~]$ X=0 ; while [ $X -le 5 ] ; do echo $((X++)) ; done
0
1
2
3
4
5
[student@studentvm1 ~]$
अनटिल लूप
until कमांड while कमांड की तरह ही है। अंतर यह है कि यह तब तक लूप करना जारी रखेगा जब तक लॉजिकल एक्सप्रेशन "सत्य" के रूप में मूल्यांकन नहीं करता। इस निर्माण के सबसे सरल रूप को देखें:
[student@studentvm1 ~]$ X=0 ; until false ; do echo $((X++)) ; done | head
0
1
2
3
4
5
6
7
8
9
[student@studentvm1 ~]$
यह एक विशिष्ट मान तक गिनने के लिए लॉजिकल तुलना का उपयोग करता है:
[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo $((X++)) ; done
0
1
2
3
4
[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo $((++X)) ; done
1
2
3
4
5
[student@studentvm1 ~]$
सारांश
इस श्रृंखला ने बैश कमांड-लाइन प्रोग्राम और शेल स्क्रिप्ट बनाने के लिए कई शक्तिशाली उपकरणों का पता लगाया है। लेकिन इसने बैश के साथ आप कई दिलचस्प चीजें कर सकते हैं, उसकी सतह को मुश्किल से खरोंचा है; बाकी आप पर निर्भर है।
मैंने पाया है कि बैश प्रोग्रामिंग सीखने का सबसे अच्छा तरीका इसे करना है। एक सरल प्रोजेक्ट ढूंढें जिसमें कई बैश कमांड की आवश्यकता हो और उनसे एक CLI प्रोग्राम बनाएं। सिसएडमिन कई ऐसे कार्य करते हैं जो CLI प्रोग्रामिंग के लिए उपयुक्त हैं, इसलिए मुझे यकीन है कि आप स्वचालित करने के लिए आसानी से कार्य ढूंढ लेंगे।
कई साल पहले, अन्य शेल भाषाओं और पर्ल से परिचित होने के बावजूद, मैंने अपने सभी सिसएडमिन ऑटोमेशन कार्यों के लिए बैश का उपयोग करने का निर्णय लिया। मैंने पाया है कि - कभी-कभी थोड़ी खोज के साथ - मैं बैश का उपयोग करके वह सब कुछ पूरा करने में सक्षम रहा हूं जिसकी मुझे आवश्यकता है।