हालांकि छोटी उपयोगी स्क्रिप्ट लिखना मजेदार है, लेकिन कभी-कभी आपको ईश्वर के प्रति ईमानदार कमांड-लाइन एप्लिकेशन लिखने की आवश्यकता होती है। एक जो तर्क लेता है और इनपुट, आउटपुट, त्रुटि रिपोर्टिंग, आदि के लिए यूनिक्स सम्मेलनों के साथ अच्छी तरह से खेलता है।
सौभाग्य से, रूबी आपको उन सभी बिल्डिंग ब्लॉक्स देता है जिनकी आपको कमांड-लाइन एप्लिकेशन को काफी आसानी से आवश्यकता होती है। इस पोस्ट में मैं विशिष्ट "हाउ टू डू एक्स विद जेम वाई" दृष्टिकोण से आगे जाने की उम्मीद करता हूं, और इसके बजाय उन सभी टुकड़ों का व्यापक अवलोकन करता हूं जो एक प्रथम-दर कमांड-लाइन ऐप बनाने के लिए एक साथ जाते हैं।
इनपुट:पर्यावरण चर
पर्यावरण चर आमतौर पर छोटे कॉन्फ़िगरेशन मानों के लिए उपयोग किए जाते हैं जिन्हें आप थोड़ी देर के लिए चिपकाना चाहते हैं। एपीआई कुंजियाँ एक अच्छा उदाहरण हैं।
पर्यावरण चर सेट करना
उपयोगकर्ता आमतौर पर बैश जैसे शेल के माध्यम से पर्यावरण चर सेट करते हैं। वे उन्हें एक विशिष्ट सत्र के लिए सेट कर सकते हैं:
$ AWS_ACCESS_KEY_ID=FOO
वे उन्हें वर्तमान सत्र के साथ-साथ किसी भी नए बैश सत्र के लिए सेट कर सकते हैं:
$ export AWS_ACCESS_KEY_ID=FOO
या वे उन्हें एक ही कार्यक्रम के लिए सेट कर सकते हैं:
$ env AWS_ACCESS_KEY_ID=FOO aws s3 mb s3://mybucket
पर्यावरण चर पढ़ना
भले ही उपयोगकर्ता पर्यावरण चर कैसे सेट करता है, आप इसे रूबी के ENV
. के माध्यम से पढ़ सकते हैं हैश।
key_id = ENV["AWS_ACCESS_KEY_ID"]
पर्यावरण संस्करण हुड के नीचे कैसे काम करते हैं?
प्रत्येक प्रक्रिया में इसके साथ जुड़े पर्यावरण चर की एक तालिका होती है। जब यह बाल प्रक्रियाओं को जन्म देता है, तो माता-पिता जो भी बदलाव करना चाहते हैं, उसके साथ उन्हें उस तालिका की एक प्रति प्राप्त होती है।
कीचड़ की तरह साफ?
अधिक पठनीय स्पष्टीकरण के लिए, मेरी अन्य ब्लॉग पोस्ट देखें:द रूबीस्ट्स गाइड टू एनवायरनमेंट वेरिएबल्स।
कमांड-लाइन तर्क
जब आप टर्मिनल में कोई प्रोग्राम चलाते हैं तो कमांड-लाइन तर्क प्रोग्राम के नाम के बाद आप जो कुछ भी डालते हैं:
$ echo foo bar baz
उपरोक्त उदाहरण में "फू", "बार" और "बाज" सभी कमांड लाइन तर्क हैं।
एक अधिक यथार्थवादी उदाहरण इस तरह दिख सकता है:
$ honeybadger deploy -v --environment staging
लेकिन यहां भी, कमांड-लाइन तर्क सिर्फ टेक्स्ट हैं। अगर हम चाहते हैं --environment staging
से मतलब कुछ भी, हमें खुद ही इसका पता लगाना होगा।
आदेश-पंक्ति तर्क भेजना
हम पहले ही इस तरह भेजे गए तर्क देख चुके हैं:
$ echo foo bar baz
लेकिन चूंकि रूबी एक व्याख्या की गई भाषा है, इसलिए आपको कुछ इस तरह का भी सामना करना पड़ सकता है:
$ ruby myprog.rb foo
जहां तक ओएस का संबंध है, आप "रूबी" नामक एक प्रोग्राम चला रहे हैं और इसे दो तर्क दे रहे हैं।
सौभाग्य से, रूबी यह जानने के लिए काफी स्मार्ट है कि तर्क "फू" आपके प्रोग्राम के लिए है, रूबी के लिए नहीं। तो आपके प्रोग्राम को एक तर्क दिखाई देगा, "foo"
।
कमांड-लाइन तर्क पढ़ना
कमांड-लाइन तर्क ARGV
. नामक एक सरणी में संग्रहीत होते हैं . यह एक वैश्विक चर है, इसलिए आप इसे अपने कार्यक्रम में कहीं से भी एक्सेस कर सकते हैं।
नीचे दिए गए उदाहरण में, हम सब कुछ ARGV
. में प्रिंट कर रहे हैं . बेझिझक इसे अपने टर्मिनल में कॉपी और पेस्ट करें और इसके साथ थोड़ा खेलें।
$ ruby -e "puts ARGV.inspect" foo bar baz
["foo", "bar", "baz"]
<ब्लॉकक्वॉट>
कृपया ruby -e
. द्वारा फेंके न जाएं व्यापार। मैं इसे यहाँ केवल इसलिए उपयोग कर रहा हूँ क्योंकि यह मुझे प्रोग्राम और इसे एक ही पंक्ति में चलाने का परिणाम दिखाने देता है।
आदेश-पंक्ति तर्क पार्स करना
कमांड लाइन तर्क सिर्फ पाठ हैं। अगर आप चाहते हैं कि टेक्स्ट का मतलब मतलब . हो कुछ भी, आपको इसे पार्स करना होगा। सौभाग्य से, कई अच्छे पुस्तकालय हैं जो पार्सिंग में आपकी सहायता कर सकते हैं।
इन वर्षों में, कमांड-लाइन तर्कों के लिए कुछ हद तक मानक वाक्यविन्यास उभरा है। यह कुछ इस तरह दिखता है:
$ program -a --option foo
इस शैली से आप -h
. जैसे बूलियन फ़्लैग प्राप्त कर सकते हैं मदद प्रदर्शित करने के लिए। यह आपको मानों के साथ विकल्प निर्दिष्ट करने देता है।
पेश है OptionParser
OptionParser
कक्षा रूबी की मानक पुस्तकालय का हिस्सा है। यह आपको ऊपर की शैली से मेल खाने वाले विकल्पों को पार्स करने का एक आसान तरीका देता है।
आइए एक सरल एप्लिकेशन बनाते हैं जो हैलो कहता है। यह आपको कमांड-लाइन तर्क के माध्यम से नाम निर्दिष्ट करने देगा।
require 'optparse'
# This will hold the options we parse
options = {}
OptionParser.new do |parser|
# Whenever we see -n or --name, with an
# argument, save the argument.
parser.on("-n", "--name NAME", "The name of the person to greet.") do |v|
options[:name] = v
end
end.parse!
# Now we can use the options hash however we like.
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|
...
# Note the `Integer` arg. That tells the parser to cast the value to an int.
# I could have used `Float`, `Date`, or a number of other types.
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 | सभी, संलग्न करें |
-d | डीबग मोड, या निर्देशिका निर्दिष्ट करें |
-e | कुछ निष्पादित करें, या इसे संपादित करें |
-f | फ़ाइल निर्दिष्ट करें, या किसी कार्रवाई को बाध्य करें। |
-h | सहायता |
-m | संदेश निर्दिष्ट करें |
-o | कोई आउटपुट फ़ाइल या डिवाइस निर्दिष्ट करें |
-q | शांत मोड |
-v | वर्बोज़ मोड। वर्तमान संस्करण प्रिंट करें |
-y | किसी भी संकेत के लिए "हां" कहें |
OptionParser
के विकल्प
OptionParser
अच्छा है, लेकिन इसकी सीमाएं हैं। सबसे स्पष्ट रूप से, यह कमांड-आधारित सिंटैक्स का समर्थन नहीं करता है जो हाल के वर्षों में लोकप्रिय हो गए हैं:
$ myprog command subcommand -V
सौभाग्य से, एक टन . हैं वहाँ से बाहर पुस्तकालयों को पार्स करने का विकल्प। मुझे यकीन है कि आप अपनी पसंद का एक ढूंढ पाएंगे। यहां तीन हैं जो दिलचस्प लग रही हैं:
- GLI - "Git-like Interface" कमांड-लाइन पार्सर आपको आसानी से ऐसे एप्लिकेशन बनाने की अनुमति देता है, जो git की तरह, कई कमांड के साथ एकल निष्पादन योग्य हैं।
- CRI - नेस्टेड कमांड के समर्थन के साथ कमांड-लाइन टूल बनाने के लिए उपयोग में आसान लाइब्रेरी।
- मेथाडोन - मेथाडोन डीएसएल को पार्स करने का अपना विकल्प प्रदान करता है, लेकिन यह उससे कहीं आगे जाता है। यह आपके लिए एक निर्देशिका संरचना, एक परीक्षण सूट और लॉगिंग स्थापित करेगा। यह आपके सीएलआई के लिए रेल की तरह है।
STDIN के साथ बड़ी मात्रा में डेटा इनपुट करना
बड़ी मात्रा में डेटा इनपुट करने के लिए कमांड-लाइन तर्क उपयुक्त नहीं हैं। उसके लिए, आप एक IO स्ट्रीम का उपयोग करना चाहेंगे। और STDIN सबसे सुविधाजनक IO स्ट्रीम में से एक है।
अधिकांश प्रोग्रामों को ऑपरेटिंग सिस्टम द्वारा स्वचालित रूप से एक एसटीडीआईएन सौंपा जाएगा। यह केवल-पढ़ने के लिए फ़ाइल की तरह है जिसका उपयोग OS आपके ऐप डेटा को भेजने के लिए करता है। इसका उपयोग उपयोगकर्ता से कीबोर्ड इनपुट प्राप्त करने के लिए किया जा सकता है। लेकिन इससे भी महत्वपूर्ण बात यह है कि इसका उपयोग पाइप के माध्यम से अन्य कार्यक्रमों से डेटा प्राप्त करने के लिए किया जा सकता है।
आप एसटीडीआईएन का उपयोग वैसे ही करते हैं जैसे आप केवल-पढ़ने के लिए फ़ाइल का उपयोग करते हैं। यहाँ, मैं अपने प्रोग्राम के STDIN में कुछ टेक्स्ट डाल रहा हूँ। फिर मैं read
. का उपयोग करता हूं इसे लाने की विधि।
$ echo "hello world" | ruby -e "puts STDIN.read.upcase"
HELLO WORLD
रूबी में, आप Enumerable
. का उपयोग कर सकते हैं आईओ वस्तुओं पर सुविधाएँ। और एसटीडीआईएन कोई अपवाद नहीं है। इसका मतलब है कि आप हर तरह के टोटके कर सकते हैं:
# Get the first 20 lines
STDIN.first(20)
# Convert to integers and reject odds
STDIN.map(&:to_i).reject(&:odd)
...etc
परिणामों को STDOUT में आउटपुट करना
STDOUT केवल लिखने के लिए IO स्ट्रीम है जिसे ऑपरेटिंग सिस्टम आपके प्रोग्राम को असाइन करता है।
STDOUT को लिखकर आप यूजर के टर्मिनल पर टेक्स्ट भेज सकते हैं। उपयोगकर्ता आउटपुट को किसी फ़ाइल में रीडायरेक्ट कर सकता है, या इसे किसी अन्य प्रोग्राम में पाइप कर सकता है।
आप STDOUT का उपयोग वैसे ही करेंगे जैसे आप किसी अन्य केवल-लिखने वाली फ़ाइल का उपयोग करते हैं:
STDOUT.write("Hi!\n")
# hi
और हां, puts
और print
दोनों आउटपुट STDOUT को।
STDERR को स्थिति की जानकारी भेजना
STDERR अभी तक केवल लिखने के लिए IO स्ट्रीम है। यह सामान्य आउटपुट के लिए नहीं है। यह विशेष रूप से स्थिति संदेशों के लिए है — इसलिए वे वास्तविक . के रास्ते में नहीं आते हैं आउटपुट।
एसटीडीईआरआर आमतौर पर उपयोगकर्ता के टर्मिनल में प्रदर्शित किया जाएगा, भले ही वे एसटीडीओयूटी को किसी फ़ाइल या किसी अन्य प्रोग्राम पर रीडायरेक्ट कर रहे हों।
नीचे दिए गए उदाहरण में हम वेबपेज लाने के लिए कर्ल का उपयोग कर रहे हैं। यह वेबपेज की सामग्री को 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
अपने स्वयं के कार्यक्रमों से एसटीडीईआरआर को लिखना आसान है। बस इसे IO ऑब्जेक्ट की तरह मानें:
STDERR.write("blah\n")
STDERR.puts("blah")
सुंदर स्टिक से मारना
अब तक हमने जो उदाहरण देखे हैं, वे किसी भी डिज़ाइन पुरस्कार को जीतने वाले नहीं हैं। लेकिन इसका कोई कारण नहीं है कि कमांड-लाइन ऐप्स बदसूरत, या गैर-संवादात्मक हों।
यदि आप अपने ऐप को बेहतर बनाने का निर्णय लेते हैं, तो भारी भारोत्तोलन करने के लिए बहुत सारे शानदार रत्न हैं। यहां तीन हैं जो मुझे पसंद हैं:
- हाईलाइन - हाईलाइन एक बेहतरीन लाइब्रेरी है जो उपयोगकर्ता इनपुट को इकट्ठा करने, मान्य करने और टाइपकास्ट करने में बहुत काम लेती है।
- command_line_reporter - ASCII प्रगति रिपोर्ट बनाना आसान बनाता है।
- पेंट - आपको अपने उबाऊ पुराने टेक्स्ट को रंगीन करने के लिए आसानी से एएनएसआई रंग कोड जोड़ने देता है।
अधिक विस्तृत सूची देखें
निकास स्थिति
यदि आपका प्रोग्राम किसी त्रुटि के साथ बाहर निकलता है, तो आपको ओएस को एक्जिट कोड के माध्यम से बताना चाहिए। यही कारण है कि इस तरह से बैश कोड काम करना संभव बनाता है:
$ prog1 && prog2
यदि आप बैश के &&
. से परिचित नहीं हैं ऑपरेटर, इसका सीधा सा अर्थ है "prog2 तभी चलाएं जब prog1 सफलतापूर्वक बाहर निकल जाए।"
रूबी डिफ़ॉल्ट रूप से "सफलता" कोड और अपवाद पर "विफलता" कोड के साथ बाहर निकलती है। बाकी सब कुछ लागू करने के लिए आप पर निर्भर है। सौभाग्य से, यह आसान है:
# Any nonzero argument to `exit` counts as failure
exit(1)
अधिक जानकारी के लिए, मेरी अन्य पोस्ट देखें कि रूबी प्रोग्राम से कैसे बाहर निकलें।
प्रक्रिया का नाम सेट करना
यदि आपका कमांड-लाइन प्रोग्राम थोड़ी देर के लिए चलने वाला है, तो यह महत्वपूर्ण है कि लोगों को पता चले कि सिस्टम प्रक्रियाओं को सूचीबद्ध करते समय यह क्या है। आम तौर पर, रूबी प्रोग्राम प्रोसेस नाम में वह सब कुछ होता है जिसे आपने प्रोग्राम चलाने के लिए टाइप किया था। तो अगर आपने 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(...)
अधिक जानकारी के लिए, मेरी पोस्ट देखें:अपनी रूबी स्क्रिप्ट की प्रक्रिया का नाम कैसे बदलें जैसा कि शीर्ष और पीएस द्वारा दिखाया गया है