छोटे वन-ऑफ यूटिलिटी स्क्रिप्ट लिखना मजेदार है, लेकिन कभी-कभी आपको एक वास्तविक, ईमानदार कमांड-लाइन एप्लिकेशन लिखने की आवश्यकता होती है। ऐसा जो आर्गुमेंट्स लेता है और इनपुट, आउटपुट, त्रुटि रिपोर्टिंग आदि के लिए यूनिक्स कन्वेंशन्स के साथ अच्छा व्यवहार करता है।
सौभाग्य से, Ruby आपको कमांड-लाइन एप्लिकेशन को काफी आसानी से बनाने के लिए सभी बिल्डिंग ब्लॉक्स देता है। इस पोस्ट में मुझे उम्मीद है कि सामान्य "जेम Y के साथ X कैसे करें" दृष्टिकोण से आगे जाकर, एक प्रथम-श्रेणी के कमांड-लाइन ऐप को बनाने वाले सभी टुकड़ों का व्यापक अवलोकन करूं।
इनपुट: पर्यावरण चर (Environment Variables)
पर्यावरण चर आमतौर पर छोटे कॉन्फिगरेशन मानों के लिए उपयोग किए जाते हैं जिन्हें आप कुछ समय के लिए रखना चाहते हैं। API कुंजियाँ एक अच्छा उदाहरण हैं।
पर्यावरण चर सेट करना
उपयोगकर्ता आमतौर पर bash जैसे शेल के माध्यम से पर्यावरण चर सेट करते हैं। वे उन्हें एक विशिष्ट सत्र के लिए सेट कर सकते हैं:
$ AWS_ACCESS_KEY_ID=FOO
वे उन्हें वर्तमान सत्र के साथ-साथ किसी भी नए bash सत्र के लिए सेट कर सकते हैं:
$ export AWS_ACCESS_KEY_ID=FOO
या वे उन्हें एक ही प्रोग्राम के लिए सेट कर सकते हैं:
$ env AWS_ACCESS_KEY_ID=FOO aws s3 mb s3://mybucket
पर्यावरण चर पढ़ना
उपयोगकर्ता पर्यावरण चर को कैसे भी सेट करे, आप इसे Ruby के ENV हैश के माध्यम से पढ़ सकते हैं।
key_id = ENV["AWS_ACCESS_KEY_ID"]
पर्यावरण चर हुड के नीचे कैसे काम करते हैं?
प्रत्येक प्रक्रिया के साथ पर्यावरण चरों की एक तालिका जुड़ी होती है। जब यह चाइल्ड प्रोसेस उत्पन्न करता है, तो उन्हें उस तालिका की एक प्रति मिलती है साथ ही पैरेंट जो भी बदलाव करना चाहता है।
स्पष्ट है न?
अधिक पठनीय व्याख्या के लिए, मेरा अन्य ब्लॉग पोस्ट देखें: The Rubyist's Guide to Environment Variables.
कमांड-लाइन आर्गुमेंट्स
कमांड-लाइन आर्गुमेंट्स वे कुछ भी हैं जो आप टर्मिनल में प्रोग्राम चलाते समय प्रोग्राम नाम के बाद डालते हैं:
$ echo foo bar baz
ऊपर दिए गए उदाहरण में "foo", "bar" और "baz" सभी कमांड-लाइन आर्गुमेंट्स हैं।
एक अधिक यथार्थवादी उदाहरण इस तरह दिख सकता है:
$ honeybadger deploy -v --environment staging
लेकिन यहां भी, कमांड-लाइन आर्गुमेंट्स सिर्फ टेक्स्ट हैं। यदि हम चाहते हैं कि --environment staging का कोई मतलब हो, तो हमें इसे स्वयं समझना होगा।
कमांड-लाइन आर्गुमेंट्स भेजना
हम पहले ही आर्गुमेंट्स को इस तरह भेजते हुए देख चुके हैं:
$ echo foo bar baz
लेकिन चूंकि Ruby एक इंटरप्रेटेड भाषा है, आप इस तरह की चीज भी देख सकते हैं:
$ ruby myprog.rb foo
जहां तक OS का सवाल है, आप "ruby" नामक एक प्रोग्राम चला रहे हैं और इसे दो आर्गुमेंट्स पास कर रहे हैं।
सौभाग्य से, Ruby यह जानने के लिए पर्याप्त स्मार्ट है कि आर्गुमेंट "foo" आपके प्रोग्राम के लिए है, Ruby के लिए नहीं। इसलिए आपका प्रोग्राम एक आर्गुमेंट देखेगा, "foo"।
कमांड-लाइन आर्गुमेंट्स पढ़ना
कमांड-लाइन आर्गुमेंट्स ARGV नामक एक सरणी में संग्रहीत होते हैं। यह एक ग्लोबल वेरिएबल है, इसलिए आप इसे अपने प्रोग्राम में कहीं से भी एक्सेस कर सकते हैं।
नीचे दिए गए उदाहरण में, हम ARGV में सब कुछ प्रिंट कर रहे हैं। इसे अपने टर्मिनल में कॉपी-पेस्ट करके थोड़ा खेलने के लिए स्वतंत्र महसूस करें।
$ ruby -e "puts ARGV.inspect" foo bar baz
["foo", "bar", "baz"]
ruby -eव्यवसाय से भ्रमित न हों। मैं यहां केवल इसका उपयोग कर रहा हूं क्योंकि यह मुझे प्रोग्राम और इसे चलाने के परिणाम को एक ही लाइन में दिखाने देता है।
कमांड-लाइन आर्गुमेंट्स पार्स करना
कमांड-लाइन आर्गुमेंट्स सिर्फ टेक्स्ट हैं। यदि आप चाहते हैं कि टेक्स्ट का कोई मतलब हो, तो आपको इसे पार्स करना होगा। सौभाग्य से, कई अच्छी लाइब्रेरी हैं जो पार्सिंग में आपकी मदद कर सकती हैं।
वर्षों में, कमांड-लाइन आर्गुमेंट्स के लिए एक कुछ हद तक मानक सिंटैक्स उभरा है। यह कुछ इस तरह दिखता है:
$ program -a --option foo
यह शैली आपको -h जैसे बूलियन फ्लैग्स रखने देती है सहायता प्रदर्शित करने के लिए। यह आपको मानों के साथ विकल्प निर्दिष्ट करने की भी अनुमति देता है।
OptionParser का परिचय
OptionParser क्लास Ruby के स्टैंडर्ड लाइब्रेरी का हिस्सा है। यह आपको ऊपर की शैली से मेल खाने वाले विकल्पों को पार्स करने का एक आसान तरीका देता है।
आइए एक सरल एप्लिकेशन बनाएं जो नमस्ते कहता है। यह आपको कमांड-लाइन आर्गुमेंट के माध्यम से नाम निर्दिष्ट करने देगा।
require 'optparse'
# यह उन विकल्पों को रखेगा जिन्हें हम पार्स करते हैं
options = {}
OptionParser.new do |parser|
# जब भी हम -n या --name देखते हैं, एक आर्गुमेंट के साथ,
# आर्गुमेंट को सेव करें।
parser.on("-n", "--name NAME", "The name of the person to greet.") do |v|
options[:name] = v
end
end.parse!
# अब हम विकल्प हैश का उपयोग जैसे चाहें कर सकते हैं।
puts "Hello #{ options[:name] }" if options[:name]
फिर जब मैं इसे चलाता हूं, तो यह बस काम करता है:
$ ruby hello.rb --name Starr
Hello Starr
$ ruby hello.rb -n Starr
Hello Starr
"सहायता" स्क्रीन जोड़ना
सहायता सुविधा जोड़ना उतना ही आसान है। हमें बस कुछ टेक्स्ट प्रदान करना है, और -h के लिए एक कमांड जोड़ना है:
OptionParser.new do |parser|
parser.banner = "Usage: hello.rb [options]"
parser.on("-h", "--help", "Show this help message") do ||
puts parser
end
...
end.parse!
अब हम सहायता मांग सकते हैं:
$ ruby hello.rb -h
Usage: hello.rb [options]
-h, --help Show this help message
-n, --name NAME The name of the person to greet.
आर्गुमेंट्स टाइपकास्टिंग
सभी कमांड-लाइन आर्गुमेंट्स स्ट्रिंग्स हैं, लेकिन कभी-कभी आप चाहते हैं कि उपयोगकर्ता आपको एक नंबर या तारीख दे। हाथ से रूपांतरण करना थकाऊ हो सकता है, इसलिए OptionParser यह आपके लिए करता है।
नीचे दिए गए उदाहरण में, मैं एक "काउंट" विकल्प जोड़ने जा रहा हूं जो उपयोगकर्ता को यह निर्दिष्ट करने देता है कि प्रोग्राम को कितनी बार नमस्ते कहना चाहिए। मैं OptionParser को Integer में कास्ट करने के लिए कह रहा हूं, जो यह करता है।
OptionParser.new do |parser|
...
# `Integer` आर्गुमेंट पर ध्यान दें। यह पार्सर को मान को इंट में कास्ट करने के लिए कहता है।
# मैं `Float`, `Date`, या कई अन्य प्रकारों का उपयोग कर सकता था।
parser.on("-c", "--count COUNT", Integer, "Repeat the message COUNT times") do |v|
options[:count] = v
end
end.parse!
if options[:name]
options.fetch(:count, 1).times do
puts "Hello #{ options[:name] }"
end
end
अब, जब मैं प्रोग्राम चलाता हूं तो मुझे कई बार नमस्ते कहा जाता है:
$ ruby hello.rb -n Starr -c 5
Hello Starr
Hello Starr
Hello Starr
Hello Starr
Hello Starr
नामकरण कन्वेंशन्स
प्रोग्रामिंग के सबसे कठिन पहलुओं में से एक यह पता लगाना हो सकता है कि चीजों का नाम क्या रखा जाए। कमांड-लाइन आर्गुमेंट्स कोई अपवाद नहीं हैं। आपकी मदद के लिए, मैंने सामान्य आर्गुमेंट्स और उनके अर्थों की एक छोटी तालिका संकलित की है:
| फ्लैग | सामान्य अर्थ |
|---|---|
-a | All, Append |
-d | डीबग मोड, या डायरेक्टरी निर्दिष्ट करें |
-e | कुछ निष्पादित करें, या संपादित करें |
-f | एक फाइल निर्दिष्ट करें, या ऑपरेशन को बाध्य करें। |
-h | सहायता |
-m | एक संदेश निर्दिष्ट करें |
-o | आउटपुट फाइल या डिवाइस निर्दिष्ट करें |
-q | शांत मोड |
-v | वर्बोज़ मोड। वर्तमान संस्करण प्रिंट करें |
-y | किसी भी प्रॉम्प्ट पर "हां" कहें |
OptionParser के विकल्प
OptionParser अच्छा है, लेकिन इसकी सीमाएं हैं। सबसे स्पष्ट रूप से, यह हाल के वर्षों में लोकप्रिय हुए कमांड-आधारित सिंटैक्स का समर्थन नहीं करता:
$ myprog command subcommand -V
सौभाग्य से, वहां बहुत सारे विकल्प पार्सिंग लाइब्रेरी हैं। मुझे यकीन है कि आपको कोई ऐसी मिल जाएगी जो आपको पसंद आए। यहां तीन दिलचस्प लग रहे हैं:
- GLI - "Git-like Interface" कमांड-लाइन पार्सर आपको आसानी से ऐसे एप्लिकेशन बनाने देता है जो, गिट की तरह, एक सिंगल एक्जीक्यूटेबल हैं जिसमें कई कमांड्स हैं।
- CRI - नेस्टेड कमांड्स के समर्थन के साथ कमांड-लाइन टूल्स बनाने के लिए एक उपयोग में आसान लाइब्रेरी।
- Methadone - Methadone अपना स्वयं का विकल्प पार्सिंग DSL प्रदान करता है, लेकिन यह उससे कहीं आगे जाता है। यह आपके लिए एक डायरेक्टरी स्ट्रक्चर, एक टेस्ट सूट और लॉगिंग सेट करेगा। यह आपके CLI के लिए Rails की तरह है।
STDIN के साथ बड़ी मात्रा में डेटा इनपुट करना
कमांड-लाइन आर्गुमेंट्स बड़ी मात्रा में डेटा इनपुट करने के लिए उपयुक्त नहीं हैं। उसके लिए, आप एक IO स्ट्रीम का उपयोग करना चाहेंगे। और STDIN सबसे सुविधाजनक IO स्ट्रीम्स में से एक है।
अधिकांश प्रोग्राम्स को ऑपरेटिंग सिस्टम द्वारा स्वचालित रूप से एक STDIN असाइन किया जाता है। यह एक रीड-ओनली फाइल की तरह है जिसका उपयोग OS आपके ऐप को डेटा भेजने के लिए करता है। इसका उपयोग उपयोगकर्ता से कीबोर्ड इनपुट प्राप्त करने के लिए किया जा सकता है। लेकिन अधिक महत्वपूर्ण बात यह है कि इसका उपयोग पाइप के माध्यम से अन्य प्रोग्राम्स से डेटा प्राप्त करने के लिए किया जा सकता है।
आप STDIN का उपयोग ठीक वैसे ही करते हैं जैसे आप रीड-ओनली फाइल का उपयोग करते। यहां, मैं अपने प्रोग्राम के STDIN में कुछ टेक्स्ट पाइप कर रहा हूं। फिर मैं इसे लाने के लिए read मेथड का उपयोग करता हूं।
$ echo "hello world" | ruby -e "puts STDIN.read.upcase"
HELLO WORLD
Ruby में, आप IO ऑब्जेक्ट्स पर Enumerable फीचर्स का उपयोग कर सकते हैं। और STDIN कोई अपवाद नहीं है। इसका मतलब है कि आप सभी प्रकार के ट्रिक्स कर सकते हैं:
# पहले 20 लाइनें प्राप्त करें
STDIN.first(20)
# इंटीजर्स में बदलें और ऑड को अस्वीकार करें
STDIN.map(&:to_i).reject(&:odd)
...आदि
STDOUT पर परिणाम आउटपुट करना
STDOUT एक राइट-ओनली IO स्ट्रीम है जिसे ऑपरेटिंग सिस्टम आपके प्रोग्राम को असाइन करता है।
STDOUT पर लिखकर, आप उपयोगकर्ता के टर्मिनल पर टेक्स्ट भेज सकते हैं। उपयोगकर्ता आउटपुट को फाइल में रीडायरेक्ट कर सकता है, या इसे दूसरे प्रोग्राम में पाइप कर सकता है।
आप STDOUT का उपयोग ठीक वैसे ही करेंगे जैसे आप किसी अन्य राइट-ओनली फाइल का उपयोग करते:
STDOUT.write("Hi!\n") # hi
और निश्चित रूप से, puts और print दोनों STDOUT पर आउटपुट करते हैं।
STDERR पर स्थिति जानकारी भेजना
STDERR एक और राइट-ओनली IO स्ट्रीम है। यह सामान्य आउटपुट के लिए नहीं है। यह विशेष रूप से स्टेटस मैसेजेस के लिए है — ताकि वे वास्तविक आउटपुट के रास्ते में न आएं।
STDERR आमतौर पर उपयोगकर्ता के टर्मिनल में प्रदर्शित होगा, भले ही वे STDOUT को फाइल या किसी अन्य प्रोग्राम में रीडायरेक्ट कर रहे हों।
नीचे दिए गए उदाहरण में हम एक वेबपेज लाने के लिए curl का उपयोग कर रहे हैं। यह वेबपेज की सामग्री को STDOUT पर आउटपुट करता है, और प्रगति जानकारी को STDERR पर प्रदर्शित करता है:
$ curl "https://www.google.com/" > /tmp/g.html
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 151k 0 151k 0 0 277k 0 --:--:-- --:--:-- --:--:-- 277k
अपने प्रोग्राम्स से STDERR पर लिखना आसान है। बस इसे उस IO ऑब्जेक्ट की तरह व्यवहार करें जो यह है:
STDERR.write("blah\n")
STDERR.puts("blah")
इसे सुंदर बनाना
हमने अब तक जो उदाहरण देखे हैं वे कोई डिज़ाइन अवार्ड नहीं जीतने वाले। लेकिन कोई कारण नहीं है कि कमांड-लाइन ऐप्स बदसूरत या गैर-इंटरैक्टिव होने चाहिए।
यदि आप अपने ऐप को सजाने का निर्णय लेते हैं, तो भारी काम करने के लिए बहुत सारे बेहतरीन जेम्स हैं। यहां तीन हैं जो मुझे पसंद हैं:
- highline - Highline एक बेहतरीन लाइब्रेरी है जो उपयोगकर्ता इनपुट को इकट्ठा करने, मान्य करने और टाइपकास्ट करने का बहुत सारा काम आसान बनाती है।
- command_line_reporter - ASCII प्रगति रिपोर्ट उत्पन्न करना आसान बनाता है।
- paint - आपको अपने उबाऊ पुराने टेक्स्ट को कलर करने के लिए ANSI कलर कोड आसानी से जोड़ने देता है।
एक अधिक विस्तृत सूची देखें
एग्जिट स्टेटस
यदि आपका प्रोग्राम किसी त्रुटि के साथ बाहर निकलता है, तो आपको एक्जिट कोड के माध्यम से OS को बताना चाहिए। यही वह चीज है जो इस तरह के bash कोड को काम करने संभव बनाती है:
$ prog1 && prog2
यदि आप bash के && ऑपरेटर से परिचित नहीं हैं, तो इसका सीधा सा मतलब है "prog2 तभी चलाएं जब prog1 सफलतापूर्वक बाहर निकला हो।"
Ruby डिफ़ॉल्ट रूप से "सफलता" कोड के साथ बाहर निकलता है, और अपवाद पर "विफलता" कोड। बाकी सब आपके ऊपर है। सौभाग्य से, यह आसान है:
# `exit` को कोई भी नॉन-जीरो आर्गुमेंट विफलता माना जाता है
exit(1)
अधिक जानकारी के लिए, मेरा अन्य पोस्ट देखें How to exit a Ruby program.
प्रोसेस नाम सेट करना
यदि आपका कमांड-लाइन प्रोग्राम थोड़ी देर चलने वाला है, तो यह महत्वपूर्ण है कि लोग जानें कि यह क्या है जब वे सिस्टम प्रोसेस सूचीबद्ध करते हैं। आमतौर पर, एक Ruby प्रोग्राम का प्रोसेस नाम वह सब कुछ होता है जिसे आपने प्रोग्राम चलाने के लिए टाइप किया था। इसलिए यदि आपने ruby myapp -v टाइप किया, तो वही प्रोसेस का नाम है।
यदि आपके प्रोग्राम में बहुत सारे आर्गुमेंट्स हैं, तो यह अपठनीय हो सकता है। इसलिए आप एक अधिक मित्रवत प्रोसेस नाम सेट करना चाह सकते हैं। आप इसे इस तरह कर सकते हैं:
Process.setproctitle("My Awesome Command Line App")
यदि आप फैंसी महसूस कर रहे हैं, तो आप प्रोसेस शीर्षक का उपयोग करके उपयोगकर्ता को किसी भी क्षण प्रोसेस क्या कर रहा है, इसकी जानकारी दे सकते हैं।
Process.setproctitle("Mail Sender: initializing")
init(...)
Process.setproctitle("Mail Sender: connecting")
connect(...)
Process.setproctitle("Mail Sender: sending")
send(...)
अधिक जानकारी के लिए, मेरी पोस्ट देखें: How to change the process name of your Ruby script as shown by top and ps