यदि आप लिनक्स या मैक का उपयोग करते हैं, तो हर बार जब आप टर्मिनल खोलते हैं, तो आप एक शेल एप्लिकेशन का उपयोग कर रहे होते हैं।
एक शेल एक इंटरफेस है जो आपको अपने सिस्टम में कमांड निष्पादित करने में मदद करता है।
शेल एनवायरनमेंट वेरिएबल्स को होस्ट करता है और इसमें कमांड हिस्ट्री और ऑटो-कम्पलीशन जैसी उपयोगी विशेषताएं होती हैं।
यदि आप उन लोगों में से हैं जो यह सीखना पसंद करते हैं कि चीजें हुड के नीचे कैसे काम करती हैं, तो यह पोस्ट आपके लिए एकदम सही होगी!
शेल कैसे काम करता है?
अपना खुद का शेल एप्लिकेशन बनाने के लिए आइए सोचते हैं कि शेल वास्तव में क्या है:
सबसे पहले, एक प्रॉम्प्ट होता है, आमतौर पर कुछ अतिरिक्त जानकारी के साथ जैसे आपका वर्तमान यूजर और वर्तमान डायरेक्टरी, फिर आप एक कमांड टाइप करते हैं और जब आप एंटर दबाते हैं तो परिणाम आपकी स्क्रीन पर प्रदर्शित होते हैं।
हां, यह बहुत बुनियादी लगता है, लेकिन क्या यह आपको किसी चीज की याद नहीं दिलाता?
यदि आप pry के बारे में सोच रहे हैं तो आप सही हैं!
एक शेल मूल रूप से आपके ऑपरेटिंग सिस्टम के लिए एक REPL (Read-Eval-Print-Loop) है।
यह जानकर हम अपने शेल का पहला संस्करण लिख सकते हैं:
prompt = "> " print prompt while (input = gets.chomp) break if input == "exit" system(input) print prompt end
यह हमें एक न्यूनतम लेकिन कार्यात्मक शेल देगा। हम एक ऐसी लाइब्रेरी का उपयोग करके इसमें सुधार कर सकते हैं जिसका उपयोग कई अन्य REPL-जैसे एप्लिकेशन करते हैं।
उस लाइब्रेरी का नाम Readline है।
Readline लाइब्रेरी का उपयोग करना
Readline रूबी स्टैंडर्ड लाइब्रेरी का हिस्सा है, इसलिए इंस्टॉल करने के लिए कुछ भी नहीं है, आपको बस इसे require करना है।
Readline का उपयोग करने का एक फायदा यह है कि यह हमारे लिए कमांड हिस्ट्री को स्वचालित रूप से रख सकता है।
यह कमांड प्रॉम्प्ट प्रिंट करने और कई अन्य चीजों का भी ख्याल रख सकता है।
यहां हमारे शेल का v2 है, इस बार Readline का उपयोग करके:
require 'readline' while input = Readline.readline("> ", true) break if input == "exit" system(input) end
यह बहुत अच्छा है, हम प्रॉम्प्ट के लिए दो puts से छुटकारा पा गए और अब हमारे पास Readline की कुछ शक्तिशाली क्षमताओं तक पहुंच है। उदाहरण के लिए, हम एक शब्द को डिलीट करने के लिए कीबोर्ड शॉर्टकट (CTRL + W) या हिस्ट्री खोजने के लिए (CTRL + R) का उपयोग कर सकते हैं!
आइए पूरी हिस्ट्री प्रिंट करने के लिए एक नया कमांड जोड़ते हैं:
require 'readline' while input = Readline.readline("> ", true) break if input == "exit" puts Readline::HISTORY.to_a if input == "hist" # ब्लैंक लाइन्स को हिस्ट्री से हटाएं Readline::HISTORY.pop if input == "" system(input) end
मजेदार तथ्य: यदि आप इस कोड को pry में आज़माते हैं तो आपको pry की कमांड हिस्ट्री मिलेगी! कारण यह है कि pry भी
Readlineका उपयोग कर रहा है, औरReadline::HISTORYशेयर्ड स्टेट है।
अब आप अपनी कमांड हिस्ट्री प्राप्त करने के लिए hist टाइप कर सकते हैं 🙂
ऑटो-कम्पलीशन जोड़ना
आपके पसंदीदा शेल के ऑटो-कम्पलीशन फीचर की बदौलत आप बहुत सारी टाइपिंग बचा पाएंगे। Readline इस फीचर को अपने शेल में इंटीग्रेट करना वास्तव में आसान बनाता है।
आइए अपनी हिस्ट्री से कमांड्स को ऑटो-कम्पलीट करके शुरू करते हैं।
उदाहरण:
comp = proc { |s| Readline::HISTORY.grep(/^#{Regexp.escape(s)}/) } Readline.completion_append_character = " " Readline.completion_proc = comp ## बाकी कोड यहां जाता है ##
इस कोड के साथ आप पहले से टाइप की गई कमांड्स को <tab> कुंजी दबाकर ऑटो-कम्पलीट कर पाएंगे। अब आइए इसे एक कदम आगे बढ़ाएं और डायरेक्टरी ऑटो-कम्पलीशन जोड़ें।
उदाहरण:
comp = proc do |s| directory_list = Dir.glob("#{s}*") if directory_list.size > 0 directory_list else Readline::HISTORY.grep(/^#{Regexp.escape(s)}/) end end
completion_proc संभावित उम्मीदवारों की सूची लौटाता है, इस मामले में हमें केवल यह जांचना है कि टाइप किया गया स्ट्रिंग Dir.glob का उपयोग करके डायरेक्टरी नाम का हिस्सा है या नहीं। Readline बाकी का ख्याल रखेगा!
सिस्टम मेथड को इम्प्लीमेंट करना
अब आपके पास एक कार्यशील शेल होना चाहिए, हिस्ट्री और ऑटो-कम्पलीशन के साथ, 25 लाइनों के कोड के लिए बुरा नहीं है 🙂
लेकिन एक चीज है जिसमें मैं गहराई से जाना चाहता हूं, ताकि आप वास्तव में एक कमांड को निष्पादित करने के पीछे के दृश्यों में क्या हो रहा है, इसके बारे में कुछ जानकारी प्राप्त कर सकें।
यह system मेथड द्वारा किया जाता है, C में यह मेथड आपके कमांड को /bin/sh पर भेजता है, जो एक शेल एप्लिकेशन है। आइए देखते हैं कि आप रूबी में /bin/sh क्या करता है, इसे कैसे इम्प्लीमेंट कर सकते हैं।
नोट: यह केवल लिनक्स/मैक पर काम करेगा 🙂
system मेथड:
def system(command) fork { exec(command) } end
यहां क्या होता है कि fork वर्तमान प्रोसेस की एक नई कॉपी बनाता है, फिर इस प्रोसेस को exec मेथड के माध्यम से हमारे द्वारा चलाए जाने वाले कमांड द्वारा प्रतिस्थापित किया जाता है। यह लिनक्स प्रोग्रामिंग में एक बहुत ही सामान्य पैटर्न है।
यदि आप फोर्क नहीं करते हैं तो वर्तमान प्रोसेस प्रतिस्थापित हो जाता है, जिसका अर्थ है कि जब आप जो कमांड चला रहे हैं (ls, cd या कुछ और) पूरा हो जाता है तो आपका रूबी प्रोग्राम इसके साथ समाप्त हो जाएगा।
आप इसे यहां होते हुए देख सकते हैं:
def system(command) exec(command) end system('ls') # यह कोड कभी नहीं चलेगा! puts "after system"
निष्कर्ष
इस पोस्ट में आपने सीखा कि एक शेल एक REPL-जैसा इंटरफेस है (सोचें irb / pry) अपने सिस्टम के साथ इंटरैक्ट करने के लिए। आपने यह भी सीखा कि शक्तिशाली Readline लाइब्रेरी का उपयोग करके अपना खुद का शेल कैसे बनाया जाए, जो हिस्ट्री और ऑटो-कम्पलीशन जैसी कई बिल्ट-इन सुविधाएं प्रदान करती है (लेकिन आपको यह परिभाषित करना होगा कि यह कैसे काम करता है)।
और उसके बाद आपने fork + exec पैटर्न के बारे में सीखा जो आमतौर पर लिनक्स प्रोग्रामिंग प्रोजेक्ट्स में उपयोग किया जाता है।
यदि आपको यह पोस्ट पसंद आई तो क्या आप मुझे एक एहसान कर सकते हैं और इसे अपने सभी रूबी दोस्तों के साथ शेयर कर सकते हैं? यह ब्लॉग को बढ़ने में मदद करेगा और अधिक लोग सीख पाएंगे 🙂