शेल एक ऐसा प्रोग्राम है जिसमें कमांड लिखकर आप ऑपरेटिंग सिस्टम से बातचीत कर सकते हैं। शेल ग्राफिकल भी हो सकते हैं; आपका डेस्कटॉप वातावरण, जिसे आप माउस या ट्रैकपैड से चलाते हैं, भी एक शेल ही है। हम कमांड-लाइन इंटरफ़ेस पर ध्यान देंगे, जहाँ आप कमांड टाइप करते हैं और नतीजे (आमतौर पर) आपके टर्मिनल पर दिखाई देते हैं।
कमांड-लाइन शेल कई तरह के होते हैं। Windows में cmd और powershell मौजूद हैं। Mac और Linux में "Unix शेल" मिलते हैं, जो कई तरह के हैं। Mac का डिफ़ॉल्ट शेल zsh है। csh एक पुराना शेल है जिसका सिंटैक्स C जैसा है। bash सबसे लोकप्रिय शेल में से एक है, खासकर Linux पर।
सबसे शुरुआती शेल में से एक बोर्न शेल, sh, है। समय के साथ यह शेल मानकीकृत हो गया, ताकि हर Unix जैसे सिस्टम पर यह उपलब्ध हो। कई शेल बोर्न शेल पर आधारित हैं, और अक्सर इंटरैक्टिव या प्रोग्रामिंग की अतिरिक्त सुविधाएँ देते हैं।
Bash बोर्न शेल पर आधारित है। इसीलिए इसे "Bourne-again shell" कहा जाता है। कई Linux सिस्टम पर यह डिफ़ॉल्ट शेल है। Bash में कई सुविधाएँ हैं जो इसे एक आनंददायक इंटरैक्टिव शेल बनाती हैं, जैसे:
यह ट्रैक bash को इंटरैक्टिव शेल की तरह इस्तेमाल करना नहीं सिखाता। यह bash की मदद से प्रोग्राम लिखना सिखाता है। ज़ोर इस बात पर रहेगा कि समस्याएँ हल करने के लिए सिर्फ bash इस्तेमाल हो, हालाँकि बाहरी टूल को कॉल करने पर कोई रोक नहीं है।
Bash अपने इनपुट से कमांड पढ़ता है; यह इनपुट आमतौर पर या तो टर्मिनल होता है या कोई फाइल। इनपुट की जो भी पंक्ति वह पढ़ता है, उसे एक कमांड माना जाता है, यानी ऐसा निर्देश जिसे पूरा करना है। (कुछ एडवांस्ड मामले भी हैं, जैसे कई पंक्तियों में फैले कमांड, जिनकी चर्चा हम आगे करेंगे।)
Bash हर पंक्ति को शब्दों में बाँटता है, जिन्हें व्हाइटस्पेस अक्षर (स्पेस और टैब) अलग करते हैं। पंक्ति का पहला शब्द उस कमांड का नाम होता है जिसे चलाना है। बाकी सारे शब्द उस कमांड के आर्गुमेंट बन जाते हैं (ऑप्शन, फाइलों के नाम, वगैरह)।
नीचे दिए गए उदाहरणों में हम आपको कुछ इंटरैक्टिव कमांड दिखाएँगे, जिन्हें आप चला सकते हैं।
# अक्षर एक कमेंट शुरू करता है।
कमेंट के बाद आने वाले सारे शब्द शेल अनदेखा कर देता है; कमेंट सिर्फ पढ़ने के लिए होता है।$ अक्षर शेल प्रॉम्प्ट दिखाता है।
अगर आप कमांड कॉपी कर रहे हैं, तो प्रॉम्प्ट को शामिल न कीजिए।# Note: It is always a good idea to run `ls` before `rm`
# to make sure you know what you are deleting.
$ rm * # Remove all files in the current directory.
# Make sure you run this in a temp directory or you can lose files!
$ ls # List files in the current directory (no output, no files).
$ touch a b c # Create files 'a', 'b' and 'c'.
$ ls # List files again; this time the outputs: 'a', 'b' and 'c'.
a b c
rm एक एप्लीकेशन है जो उसे दी गई फाइलों को हटा देता है।
* एक ग्लॉब अक्षर है, एक "वाइल्डकार्ड" पैटर्न जो मौजूदा डायरेक्टरी की सभी फाइलों से मेल खाता है।
ग्लॉब के बारे में हम आगे और बात करेंगे।
क्या आपने ध्यान दिया कि a और b के बीच कई स्पेस हैं, और b और c के बीच सिर्फ एक?
आर्गुमेंट के बीच कितना व्हाइटस्पेस है, इससे कोई फर्क नहीं पड़ता!
जैसे:
$ echo This is a test.
This is a test.
$ echo This is a test.
This is a test.
echo एक कमांड है जो अपने आर्गुमेंट को स्टैंडर्ड आउटपुट पर प्रिंट करता है (हमारे मामले में यह टर्मिनल ही है)।
Bash इस कमांड को पाँच शब्दों में बाँटता है और echo कमांड को चार आर्गुमेंट के साथ कॉल करता है।
दूसरे उदाहरण में दिए अतिरिक्त स्पेस से कोई फर्क नहीं पड़ता।
echo को पता ही नहीं होता कि शब्दों के बीच पहले कितने स्पेस थे।
अगर हमें अतिरिक्त व्हाइटस्पेस बनाए रखना है, तो पूरे वाक्य को एक ही आर्गुमेंट की तरह भेजना होगा। ऐसा हम कोट्स का इस्तेमाल करके कर सकते हैं:
$ echo "This is a test."
This is a test.
कोट्स अपने अंदर की हर चीज़ को एक ही आर्गुमेंट में बदल देते हैं।
यह आर्गुमेंट This is a test. है, जिसमें वे सारे स्पेस अक्षर भी शामिल हैं।
ध्यान दीजिए कि कोट्स इस आर्गुमेंट का हिस्सा नहीं होते। bash उस एक आर्गुमेंट को echo को सौंपने से पहले कोट्स हटा देता है।
आर्गुमेंट को कमांड के नाम से और एक-दूसरे से व्हाइटस्पेस अलग करता है। यह बात याद रखना ज़रूरी है। जैसे, नीचे दिया गया गलत है:
$ [-f file]
bash: [-f: command not found
यहाँ उद्देश्य यह जाँचना है कि file नाम की कोई फाइल मौजूद है या नहीं।
यह मान लेना स्वाभाविक है कि [ और ] के आसपास के व्हाइटस्पेस से कोई फर्क नहीं पड़ता, लेकिन [ वास्तव में एक कमांड है, और उसका अंतिम आर्गुमेंट ] होना ज़रूरी है।
([ कमांड के बारे में हम आगे और विस्तार से बात करेंगे।)
इसलिए हमें [ को -f से और ] को file से अलग रखना होगा।
वरना bash समझेगा कि हम [-f नाम का कोई कमांड चलाने की कोशिश कर रहे हैं, जिसका एक ही आर्गुमेंट file] है।
सही कमांड हर आर्गुमेंट को व्हाइटस्पेस से अलग करता है:
$ [ -f file ]
(हमने बहुत से लोगों को इस व्यवहार से उलझते देखा है; उन्हें लगता है कि [...] सिर्फ "सिंटैक्स" है और स्पेस हटाए जा सकते हैं; जबकि [ एक कमांड है और व्हाइटस्पेस ज़रूरी है।)
अगर हमारी फाइल के नाम में व्हाइटस्पेस या कोई और खास अक्षर हो, तो उसे भी कोट्स में रखना चाहिए:
$ [ -f "my file" ]
Bash कई तरह के कमांड समझता है: एलियस, फंक्शन, बिल्टइन, कीवर्ड और एक्ज़ीक्यूटेबल। ये सब कमांड हैं, और सब लगभग एक ही तरह काम करते हैं।
डिफ़ॉल्ट रूप से एलियस सिर्फ इंटरैक्टिव शेल में चालू रहते हैं, शेल स्क्रिप्ट में नहीं। इनकी चर्चा हम यहाँ नहीं करेंगे।
एक फंक्शन में शेल कमांड होते हैं, और यह किसी छोटी स्क्रिप्ट की तरह काम करता है; यह आर्गुमेंट भी ले सकता है और लोकल वेरिएबल भी बना सकता है। जब किसी फंक्शन को कॉल किया जाता है, तो उसके अंदर के कमांड चलते हैं। फंक्शन पर हम आगे विस्तार से चर्चा करेंगे।
Bash में कुछ बुनियादी कमांड पहले से बने हुए हैं, जैसे cd (डायरेक्टरी बदलना), echo (आउटपुट लिखना) वगैरह।
इन्हें ऐसे फंक्शन मान सकते हैं जो पहले से दिए हुए हैं।
कीवर्ड बिल्टइन जैसे ही होते हैं, फर्क बस इतना है कि कीवर्ड वास्तव में bash का सिंटैक्स होते हैं और इन्हें खास नियमों के हिसाब से पार्स किया जा सकता है।
जैसे, [ एक bash बिल्टइन है, जबकि [[ एक bash कीवर्ड है; दोनों का इस्तेमाल कई तरह की शर्तें जाँचने के लिए होता है।
लेकिन [[ कीवर्ड में कुछ अतिरिक्त सुविधाएँ हैं।
जैसे, यह =~ ऑपरेटर देता है, जो रेगुलर एक्सप्रेशन की जाँच करता है।
$ [[ "hello world" =~ e.*o ]] # returns a "success" status
$ [ "hello world" =~ e.*o ]
bash: [: =~: binary operator expected
स्ट्रिंग की तुलना करने वाले ऑपरेटर < और > इसका एक और उदाहरण हैं कि कीवर्ड कोड को अलग ढंग से कैसे पार्स करते हैं।
[[...]] के अंदर इन ऑपरेटरों के लिए कोई खास सावधानी की ज़रूरत नहीं होती।
$ if [[ "$USER" < "n" ]]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
लेकिन [...] के अंदर आपको इन्हें कोट या एस्केप करना होगा, ताकि शेल इन्हें रीडायरेक्शन ऑपरेटर की तरह पार्स न कर ले।
$ if [ "$USER" < "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => bash: n: No such file or directory
$ if [ "$USER" \< "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
bash से चलाए जा सकने वाले कमांड की अंतिम किस्म एक्ज़ीक्यूटेबल है।
ये आपके कंप्यूटर के फाइलसिस्टम पर मौजूद एक्ज़ीक्यूटेबल फाइलें (प्रोग्राम) होती हैं, जिन्हें आप चला सकते हैं।
(एक्ज़ीक्यूटेबल को बाहरी कमांड या एप्लीकेशन भी कहा जाता है।)
एक्ज़ीक्यूटेबल को आमतौर पर सिर्फ उसका नाम टाइप करके चलाया जाता है; फाइल का पूरा पाथ देना आमतौर पर ज़रूरी नहीं होता।
ऐसा इसलिए हो पाता है क्योंकि bash एक पहले से तय वेरिएबल इस्तेमाल करता है, जो आम एक्ज़ीक्यूटेबल फाइलों की जगह ढूँढने में उसकी मदद करता है।
इस वेरिएबल का नाम PATH है।
यह डायरेक्टरी नामों का एक समूह है, जिन्हें कोलन से अलग किया जाता है (जैसे /usr/bin:/bin)।
जब कोई कमांड बिना फाइल पाथ के दिया जाता है (जैसे myprogram या ls), और वह एलियस, फंक्शन, बिल्टइन या कीवर्ड नहीं है, तो bash PATH की डायरेक्टरियों में खोजता है।
डायरेक्टरियों को बाएँ से दाएँ क्रम में खोजा जाता है, यह देखने के लिए कि उनमें दिए गए नाम का कोई एक्ज़ीक्यूटेबल है या नहीं।
अगर एक्ज़ीक्यूटेबल किसी जानी-पहचानी पाथ के बाहर है, तो उसका फाइल पाथ देना पड़ेगा।
मौजूदा डायरेक्टरी में मौजूद myprogram एक्ज़ीक्यूटेबल के लिए ./myprogram लिखिए; अगर यह /opt/somedirectory डायरेक्टरी में है, तो /opt/somedirectory/myprogram लिखिए।
सुझाव: कमांड के बारे में जानकारी पाने के लिए type कमांड इस्तेमाल किया जा सकता है:
$ type rm
rm is hashed (/bin/rm)
# or it might return: `rm is /bin/rm`
$ type cd
cd is a shell builtin
स्क्रिप्ट एक फाइल में रखे गए कमांड का क्रम होती है। Bash फाइल पढ़ता है और कमांड को क्रम से चलाता है। यह अगले कमांड पर तभी आगे बढ़ता है जब मौजूदा कमांड खत्म हो जाता है। अपवाद तब है जब किसी कमांड को एसिंक्रोनस रूप से (बैकग्राउंड में) चलाने के लिए कहा गया हो। अभी इस मामले की ज़्यादा चिंता न कीजिए; यह कैसे काम करता है, वह हम आगे सीखेंगे।
इस गाइड में दिया कोई भी कमांड लाइन उदाहरण स्क्रिप्ट में भी इस्तेमाल किया जा सकता है।
स्क्रिप्ट बनाना आसान है। एक नई फाइल बनाइए और उसकी पहली पंक्ति में यह लिखिए:
#!/bin/bash
इस पंक्ति को इंटरप्रेटर डायरेक्टिव कहा जाता है (इसे हैशबैंग या शीबैंग भी कहते हैं)।
यह बताती है कि जब इस फाइल को किसी कमांड में एक्ज़ीक्यूटेबल की तरह इस्तेमाल किया जाए, तो /bin/bash को इंटरप्रेटर के रूप में इस्तेमाल करना है।
जब OS का कर्नेल किसी नॉन-बाइनरी फाइल को चलाता है, तो वह फाइल की पहली पंक्ति पढ़ता है।
अगर पंक्ति #! से शुरू होती है, तो कर्नेल उसी पंक्ति से तय करता है कि फाइल किस इंटरप्रेटर को सौंपी जाए।
(ऐसा करने के और भी सही तरीके हैं, नीचे देखिए।)
#! फाइल की बिल्कुल शुरुआत में होना चाहिए, उससे पहले कोई स्पेस या खाली पंक्ति नहीं।
हमारी स्क्रिप्ट के कमांड इसके नीचे अलग-अलग पंक्तियों में आएँगे।
सुझाव: #!/bin/bash की जगह आप यह लिख सकते हैं
#!/usr/bin/env bash
env अपने पहले आर्गुमेंट में दिए नाम का एक्ज़ीक्यूटेबल $PATH में खोजता है (इस मामले में "bash")।
इस तरीके की और विस्तृत जानकारी, और यह सादे #!/bin/bash से कैसे अलग है, इसके लिए StackOverflow का यह सवाल देखिए।
इंटरनेट पर मिलने वाली उन स्क्रिप्ट या उदाहरणों से धोखा न खाइए जो इंटरप्रेटर के रूप में /bin/sh इस्तेमाल करते हैं।
sh कोई bash नहीं है!
Bash खुद एक "sh-संगत" शेल है, यानी यह ज़्यादातर sh स्क्रिप्ट चला सकता है और उसका बहुत सारा सिंटैक्स इस्तेमाल करता है।
लेकिन इसका उल्टा सच नहीं है; bash की कुछ सुविधाएँ sh में टूट जाएँगी या अप्रत्याशित व्यवहार करेंगी।
यह भी ध्यान रखिए कि कोई एक ही sh शेल नहीं है।
कई शेल ऐसे हैं जो POSIX-sh सिंटैक्स को सपोर्ट करते हैं।
आपके OS के हिसाब से, जब आप sh चलाते हैं, तब वास्तव में bash, ash, dash या कोई और शेल चल रहा हो सकता है।
स्क्रिप्ट फाइल बन जाने के बाद उसे इस तरह चलाया जा सकता है:
$ bash myscript
इस उदाहरण में हम कमांड bash चलाते हैं और उसे बताते हैं कि हमारे दिए आर्गुमेंट में मौजूद स्क्रिप्ट को पढ़े।
जब हम ऐसा करते हैं, तो #! वाली पंक्ति सिर्फ एक कमेंट रह जाती है।
दूसरा तरीका यह है कि हम अपनी स्क्रिप्ट को चलाने की अनुमति दे दें। इस तरीके से हम bash को खुद से कॉल करने की जगह स्क्रिप्ट को किसी और एक्ज़ीक्यूटेबल की तरह चला सकते हैं:
$ chmod +x myscript # Mark the file as executable.
$ ./myscript # Now, myscript can be executed directly
# instead of having to specify bash.
जब हम कमांड इस तरह चलाते हैं, तो ऑपरेटिंग सिस्टम (OS) #! वाली पंक्ति से तय करता है कि आपकी स्क्रिप्ट किस इंटरप्रेटर से चलानी है।
इस पाठ की सामग्री Bash Guide के Commands and Arguments से ली गई है।