रूबी में वास्तव में सस्ता कमांड-लाइन विकल्प पार्सिंग


114

संपादित करें: कृपया, कृपया , कृपया जवाब देने से पहले इस पोस्ट के नीचे सूचीबद्ध दो आवश्यकताओं पढ़ें। लोग अपने नए रत्न और पुस्तकालय और व्हाट्सएप पोस्ट करते रहते हैं, जो स्पष्ट रूप से आवश्यकताओं को पूरा नहीं करते हैं।

कभी-कभी मैं बहुत सस्ते में कुछ कमांड लाइन विकल्पों को एक साधारण स्क्रिप्ट में हैक करना चाहता हूं। इसे करने का एक मजेदार तरीका है, बिना गेटअप या पार्सिंग या इस तरह की किसी भी चीज़ से निपटने के लिए:

...
$quiet       = ARGV.delete('-d')
$interactive = ARGV.delete('-i')
...
# Deal with ARGV as usual here, maybe using ARGF or whatever.

यह बिल्कुल सामान्य यूनिक्स विकल्प वाक्यविन्यास नहीं है, क्योंकि यह " myprog -i foo bar -q" के रूप में विकल्प गैर-विकल्प कमांड लाइन मापदंडों को स्वीकार करेगा , लेकिन मैं इसके साथ रह सकता हूं। (कुछ लोग, जैसे कि तोड़फोड़ करने वाले डेवलपर्स, इसे पसंद करते हैं। कभी-कभी मैं भी करता हूं।)

एक विकल्प जो अभी मौजूद है या अनुपस्थित है उसे ऊपर की तुलना में बहुत अधिक लागू नहीं किया जा सकता है। (एक असाइनमेंट, एक फंक्शन कॉल, एक साइड इफ़ेक्ट।) क्या ऐसे विकल्पों से निपटने के लिए एक समान सरल तरीका है जो एक पैरामीटर लेते हैं, जैसे कि " -f फ़ाइल नाम "?

संपादित करें:

एक बिंदु जो मैंने पहले नहीं बनाया था, क्योंकि यह मेरे लिए स्पष्ट नहीं हो गया था जब तक कि ट्रोलोप के लेखक ने उल्लेख नहीं किया था कि पुस्तकालय "एक [800-पंक्ति] फ़ाइल" में फिट है, यह है कि मैं न केवल साफ देख रहा हूं सिंटैक्स, लेकिन निम्नलिखित विशेषताओं वाली एक तकनीक के लिए:

  1. कोड की सम्पूर्णता, ताकि एक में एक एकल फाइल ड्रॉप कर सकते हैं (वास्तविक स्क्रिप्ट ही है, जो केवल दर्जन लाइनों की एक जोड़ी हो सकता है को प्रभावित किए बिना) स्क्रिप्ट फ़ाइल में शामिल किया जा सकता है binएक मानक रूबी 1.8 के साथ किसी भी सिस्टम पर dir । [५- and] स्थापना और इसका उपयोग करें। यदि आप एक ऐसी रूबी स्क्रिप्ट नहीं लिख सकते हैं, जिसमें बयानों की आवश्यकता न हो और जहां कुछ विकल्पों को पार्स करने के लिए कोड एक दर्जन लाइनों के तहत हो या तो, आप इस आवश्यकता को विफल करते हैं।

  2. कोड काफी छोटा और सरल है कि कोई इसे कोड में सीधे टाइप करने के लिए पर्याप्त याद रख सकता है जो कहीं और से काटने और चिपकाने के बजाय चाल को करेगा। उस स्थिति के बारे में सोचें, जहां आप बिना इंटरनेट के उपयोग के साथ गंभीर रूप से फ़ायरवॉल के कंसोल पर हैं, और आप उपयोग करने के लिए क्लाइंट के लिए एक त्वरित स्क्रिप्ट को एक साथ टॉस करना चाहते हैं। मैं आपके बारे में नहीं जानता, लेकिन (ऊपर की आवश्यकता को विफल करने के अलावा) सरलीकृत माइक्रो-ऑप्टपर्स की 45 लाइनों को भी याद रखना कुछ ऐसा नहीं है जिसकी मुझे परवाह है।


2
गेटअप्लोंग के खिलाफ आपत्ति के लिए उत्सुक हैं?
मार्क केरी

की क्रिया। गेटटॉपलॉग के साथ, कभी-कभी विकल्प पार्सिंग कोड उस स्क्रिप्ट के भाग से अधिक लंबा होता है जो वास्तव में काम करता है। यह केवल एक सौंदर्यवादी मुद्दा नहीं है, बल्कि रखरखाव लागत का मुद्दा है।
cjs

8
मैं स्क्रिप्ट शामिल किए जाने की आवश्यकता को समझते नहीं है - दोनों getoptlongऔर optparseमानक माणिक पुस्तकालय में हैं, इसलिए आप जब अपनी स्क्रिप्ट की तैनाती उन्हें नकल की जरूरत नहीं है - अगर माणिक उस मशीन पर काम करता है, तो require 'optparse'या require 'getoptlong'भी काम करेगा।
रैंपियन

देखें stackoverflow.com/questions/21357953/… , साथ ही विलियम मॉर्गन का ट्रोलॉप के बारे में नीचे दिया गया जवाब।
निर्भय_फूल

@ कर्टसैम्पसन मैं विश्वास नहीं कर सकता कि कितने लोगों ने आपके प्रश्न का उत्तर नहीं दिया। किसी भी तरह से, अंत में 3 पोस्ट XD XD के बारे में एक अच्छा जवाब मिला
OneChillDude

जवाबों:


235

ट्रोलोप के लेखक के रूप में , मुझे लगता है कि लोगों को लगता है कि एक विकल्प पार्सर में उचित है विश्वास नहीं कर सकता। गंभीरता से। यह मन को कचोटता है।

मुझे ऐसा मॉड्यूल क्यों बनाना चाहिए जो विकल्पों को पार्स करने के लिए कुछ अन्य मॉड्यूल का विस्तार करता है? मुझे किसी भी चीज को उप-वर्ग क्यों करना चाहिए? मुझे कमांड लाइन को पार्स करने के लिए कुछ "फ्रेमवर्क" की सदस्यता क्यों लेनी चाहिए?

यहाँ ऊपर के Trollop संस्करण है:

opts = Trollop::options do
  opt :quiet, "Use minimal output", :short => 'q'
  opt :interactive, "Be interactive"
  opt :filename, "File to process", :type => String
end

और बस। optsअब कुंजी के साथ एक हैश है :quiet, :interactive, और :filename। आप इसके साथ जो चाहें कर सकते हैं। और आपको एक सुंदर मदद पृष्ठ मिलता है, जो आपकी स्क्रीन की चौड़ाई, स्वचालित लघु तर्क नामों, प्रकार की जाँच करने के लिए स्वरूपित ... सब कुछ जो आपको चाहिए।

यह एक फ़ाइल है, इसलिए यदि आप एक औपचारिक निर्भरता नहीं चाहते हैं, तो आप इसे अपने परिवाद / निर्देशिका में छोड़ सकते हैं। इसमें एक न्यूनतम डीएसएल है जिसे उठाना आसान है।

एलओसी प्रति विकल्प लोग। यह मायने रखता है।


39
BTW, +1 को ट्रोलोप (जो पहले ही यहां बताया गया था) लिखा है, लेकिन पहले पैराग्राफ को थोड़ा नीचे करने के लिए स्वतंत्र महसूस करें।
cjs

33
मुझे डर है कि इस मामले में मुझे शिकायत करने का अधिकार है। जब आप विकल्पों को देखते हैं: [1 ] [२ ] [३ ], जो मूल रूप से सिर्फ एक साधारण स्ट्रिंग सरणी को संसाधित कर रहा है (वास्तव में, उस सिंक को छोड़ दें), तो आप मदद नहीं कर सकते, लेकिन आश्चर्य क्यों? आप उस ब्लोट से क्या हासिल करते हैं? यह सी नहीं है, जहां तार हैं, "समस्याग्रस्त"। बेशक प्रत्येक अपने स्वयं के लिए। :)
srcspider

50
कृपया इसे थोड़ा नीचे न करें। यह एक धर्मी है, भाई।
विलियम पीट्री

7
दसवें शब्द को थोड़ा नीचे करने के लिए स्वतंत्र महसूस करें।
एंड्रयू ग्रिम

3
ट्रोलॉप के लिए +1। मैं इसे अपने परीक्षण स्वचालन प्रणाली के लिए उपयोग करता हूं और यह जस्ट वर्क्स। इसके साथ ही यह कोड करना इतना आसान है कि कभी-कभी मैं इसके आनंद का अनुभव करने के लिए अपने बैनर को फिर से व्यवस्थित करता हूं।
किनोफ्रॉस्ट

76

मैं आपकी अरुचि को साझा करता हूं require 'getopts', मुख्य रूप से इस तरह की विषमता के कारण OptionParser:

% cat temp.rb                                                            
require 'optparse'
OptionParser.new do |o|
  o.on('-d') { |b| $quiet = b }
  o.on('-i') { |b| $interactive = b }
  o.on('-f FILENAME') { |filename| $filename = filename }
  o.on('-h') { puts o; exit }
  o.parse!
end
p :quiet => $quiet, :interactive => $interactive, :filename => $filename
% ruby temp.rb                                                           
{:interactive=>nil, :filename=>nil, :quiet=>nil}
% ruby temp.rb -h                                                        
Usage: temp [options]
    -d
    -i
    -f FILENAME
    -h
% ruby temp.rb -d                                                        
{:interactive=>nil, :filename=>nil, :quiet=>true}
% ruby temp.rb -i                                                        
{:interactive=>true, :filename=>nil, :quiet=>nil}
% ruby temp.rb -di                                                       
{:interactive=>true, :filename=>nil, :quiet=>true}
% ruby temp.rb -dif apelad                                               
{:interactive=>true, :filename=>"apelad", :quiet=>true}
% ruby temp.rb -f apelad -i                                              
{:interactive=>true, :filename=>"apelad", :quiet=>nil}

6
धन्यवाद, मैं यह नहीं देख सकता कि यह ओपी के अनुरोध को कैसे फिट नहीं करता है, विशेष रूप से मानक लिबास में सभी पर विचार करते हुए, किसी भी गैर मानक कोड को स्थापित करने / रखने की आवश्यकता की तुलना में
dolzenko

3
यह ट्रोलॉप संस्करण की तरह ही दिखता है, इसके अलावा इसे अतिरिक्त फ़ाइल की आवश्यकता नहीं है।
क्लाउडीयू

59

यहां वह मानक तकनीक है जिसका उपयोग मैं आमतौर पर करता हूं:

#!/usr/bin/env ruby

def usage(s)
    $stderr.puts(s)
    $stderr.puts("Usage: #{File.basename($0)}: [-l <logfile] [-q] file ...")
    exit(2)
end

$quiet   = false
$logfile = nil

loop { case ARGV[0]
    when '-q' then  ARGV.shift; $quiet = true
    when '-l' then  ARGV.shift; $logfile = ARGV.shift
    when /^-/ then  usage("Unknown option: #{ARGV[0].inspect}")
    else break
end; }

# Program carries on here.
puts("quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}")

3
सवाल का जवाब देता है, लेकिन यार, ट्रोलोप से निपटना बहुत आसान लगता है। जब प्रबल पहिया इतना चिकना होता है तो पहिए को क्यों मजबूत किया जाता है?
मिकी टीके

7
प्रीमियर व्हील स्मूद नहीं है। आवश्यकताओं पर सावधानीपूर्वक ध्यान देते हुए प्रश्न को फिर से ध्यान से पढ़ें।
cjs

2
+1 कभी-कभी आपको पहिया को सुदृढ़ करने की आवश्यकता होती है, क्योंकि आप नहीं चाहते हैं या बस ट्रोलोप जैसी अन्य निर्भरता का उपयोग नहीं कर सकते हैं
लाजप

ट्रोलॉप को एक रत्न के रूप में स्थापित करने की आवश्यकता नहीं है। आप बस एक फ़ाइल को अपने libफ़ोल्डर या कोड में छोड़ सकते हैं और रूबीगम्स को छूने के बिना भी इसका उपयोग कर सकते हैं।
ओवरब्रिज

मेरे लिए, मैं बदलना पड़ा when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")करने के लिए when /^-/ then usage("Unknown option: #{ARGV.shift.inspect}")या यह एक अनंत उपयोग लूप में मिलेगा
केसी

36

चूंकि कोई भी इसका उल्लेख नहीं करता था , और शीर्षक सस्ती कमांड-लाइन पार्सिंग का उल्लेख करता है , तो बस रूबी दुभाषिए को आपके लिए काम करने की अनुमति क्यों न दें? यदि आप -sस्विच को पास करते हैं (उदाहरण के लिए, आपके शेबंग में), तो आपको मुफ्त में गंदगी-सरल स्विच मिलते हैं, जो एकल-अक्षर वैश्विक चर के लिए असाइन किए जाते हैं। यहां उस स्विच का उपयोग करके आपका उदाहरण दिया गया है:

#!/usr/bin/env ruby -s
puts "#$0: Quiet=#$q Interactive=#$i, ARGV=#{ARGV.inspect}"

और यहाँ उत्पादन है जब मैं ./testइसे बचाने के लिए और इसे chmod +x:

$ ./test
./test: Quiet= Interactive=, ARGV=[]
$ ./test -q foo
./test: Quiet=true Interactive=, ARGV=["foo"]
$ ./test -q -i foo bar baz
./test: Quiet=true Interactive=true, ARGV=["foo", "bar", "baz"]
$ ./test -q=very foo
./test: Quiet=very Interactive=, ARGV=["foo"]

देख ruby -hविवरण लिए देखें।

यह उतना ही सस्ता होना चाहिए जितना कि यह मिलता है। यदि आप एक स्विच की तरह की कोशिश करते हैं तो यह NameError बढ़ाएगा-: , इसलिए वहां कुछ सत्यापन है। बेशक, आपके पास एक गैर-स्विच तर्क के बाद कोई स्विच नहीं हो सकता है, लेकिन अगर आपको कुछ फैंसी की आवश्यकता है, तो आपको वास्तव में न्यूनतम OptionParser पर उपयोग करना चाहिए। वास्तव में, केवल एक चीज जो मुझे इस तकनीक के बारे में बताती है, वह यह है कि आपको एक अनसेट ग्लोबल वैरिएबल एक्सेस करते समय एक चेतावनी मिलेगी (यदि आपने उन्हें सक्षम कर दिया है), लेकिन यह अभी भी गलत है, इसलिए यह थ्रो टूल और त्वरित के लिए ठीक काम करता है स्क्रिप्ट।

फेलिपक द्वारा " रूबी में वास्तव में सस्ते कमांड-लाइन विकल्प पार्सिंग करने के विकल्प " में एक कैविएट ने बताया , कि आपका शेल 3-टोकन शेबंग का समर्थन नहीं कर सकता है; आपको /usr/bin/env ruby -wअपने रूबी (जैसे /usr/local/bin/ruby -w) के लिए वास्तविक पथ से बदलने की आवश्यकता हो सकती है , या इसे एक आवरण स्क्रिप्ट, या कुछ और से चला सकते हैं।


2
धन्यवाद :) मुझे यकीन है कि वह पिछले दो वर्षों से इस उत्तर की प्रतीक्षा नहीं कर रहा है
डार्कहार्ट

3
मैं वास्तव में पिछले दो वर्षों से इस उत्तर की प्रतीक्षा कर रहा हूं। :-) अधिक गंभीरता से, यह उस चतुर सोच की तरह है जिसकी मुझे तलाश थी। चेतावनी की बात थोड़ी परेशान करने वाली है, लेकिन मैं इसे कम करने के तरीकों के बारे में सोच सकता हूं।
cjs

खुशी है कि मैं (अंततः) मदद कर सकता हूं, @ कर्टसैम्पसन, एमआरआई के झंडे सीधे पर्ल से बाहर निकल गए हैं, जहां वे शेल वन-लाइनर्स में gratuitously इस्तेमाल किया जाता है। यदि आपका जवाब अभी भी आपके लिए उपयोगी है, तो स्वीकार करने के लिए स्वतंत्र महसूस करें। :)
bjjb

1
आप लिनक्स में एक शबंग में कई तर्कों का उपयोग नहीं कर सकते हैं। / usr / bin / env: 'ruby -s': ऐसी कोई फ़ाइल या निर्देशिका नहीं
FelipeC

13

मैंने लघु-विकल्प के लिए इस स्पष्ट आवश्यकता को भरने के लिए माइक्रो-ऑप्टपर्स बनाया , लेकिन विकल्प-पार्सर का उपयोग करना आसान है। इसमें ट्रोलोप के समान एक वाक्यविन्यास है और यह 70 लाइनों की छोटी है। यदि आपको सत्यापन की आवश्यकता नहीं है और खाली लाइनों के बिना कर सकते हैं तो आप इसे 45 लाइनों तक काट सकते हैं। मुझे लगता है कि वास्तव में आप क्या देख रहे थे।

संक्षिप्त उदाहरण:

options = Parser.new do |p|
  p.version = "fancy script version 1.0"
  p.option :verbose, "turn on verbose mode"
  p.option :number_of_chairs, "defines how many chairs are in the classroom", :default => 1
  p.option :room_number, "select room number", :default => 2, :value_in_set => [1,2,3,4]
end.process!

स्क्रिप्ट को कॉल करना -hया --helpप्रिंट करना

Usage: micro-optparse-example [options]
    -v, --[no-]verbose               turn on verbose mode
    -n, --number-of-chairs 1         defines how many chairs are in the classroom
    -r, --room-number 2              select room number
    -h, --help                       Show this message
    -V, --version                    Print version

यह जांचता है कि इनपुट डिफ़ॉल्ट मान के समान प्रकार का है या नहीं, छोटे और लंबे एक्सेसर्स उत्पन्न करता है, यदि अमान्य तर्क दिए गए हैं और अधिक हैं तो वर्णनात्मक त्रुटि संदेश प्रिंट करता है।

मैंने अपनी समस्या के लिए प्रत्येक विकल्प-पार्सर का उपयोग करके कई विकल्प-पार्सर की तुलना की। जानकारीपूर्ण निर्णय लेने के लिए आप इन उदाहरणों और मेरे सारांश का उपयोग कर सकते हैं। सूची में अधिक कार्यान्वयन जोड़ने के लिए स्वतंत्र महसूस करें। :)


पुस्तकालय अपने आप को ऐसा लगता है कि यह बहुत अच्छा हो सकता है। हालांकि, ट्रोलोप के साथ लाइन काउंट की तुलना करना असंगत नहीं है क्योंकि आप optparse1937 लाइनों पर निर्भर करते हैं और आवश्यकता होती है।
टेलीमैकस

6
लाइन काउंट्स की तुलना करना बिल्कुल ठीक है, क्योंकि optparseयह एक डिफॉल्ट लाइब्रेरी है, यानी यह हर रूबी इंस्टॉलेशन के साथ काम करती है। Trollopएक तृतीय पक्ष लाइब्रेरी है, इसलिए आपको हर बार जब आप किसी प्रोजेक्ट में इसे शामिल करना चाहते हैं तो पूरा कोड आयात करना होगा। µ-Optparse को हमेशा ~ 70 लाइनों की आवश्यकता होती है, क्योंकि optparseपहले से ही वहां मौजूद है।
फ्लोरियन पिल्ज़

8

मैं पूरी तरह से समझता हूं कि आप ऑप्टपर्स से बचना क्यों चाहते हैं - यह बहुत अधिक हो सकता है। लेकिन कुछ "लाइटर" समाधान (ऑप्टपर्स की तुलना में) हैं जो पुस्तकालयों के रूप में आते हैं लेकिन एक एकल रत्न स्थापना को सार्थक बनाने के लिए पर्याप्त सरल हैं।

उदाहरण के लिए, यह OptiFlag उदाहरण देखें । प्रसंस्करण के लिए बस कुछ लाइनें। आपके मामले के अनुसार एक छोटा छोटा उदाहरण:

require 'optiflag'

module Whatever extend OptiFlagSet
  flag "f"
  and_process!
end 

ARGV.flags.f # => .. whatever ..

कर रहे हैं अनुकूलित उदाहरण के टन भी । मुझे याद है कि एक और का उपयोग करना और भी आसान था, लेकिन यह मुझे अभी तक बच गया है, लेकिन मैं वापस आऊंगा और अगर मुझे यह मिल जाए तो एक टिप्पणी यहां जोड़ दूंगा।


स्पष्ट प्रश्न के बेहतर उत्तर के लिए अपने उत्तर को संपादित करने के लिए स्वतंत्र महसूस करें।
cjs

4

यह वही है जो मैं वास्तव में, वास्तव में सस्ते आर्ग्स के लिए उपयोग करता हूं:

def main
  ARGV.each { |a| eval a }
end

main

इसलिए अगर आप programname foo barइसे चलाते हैं तो आपको फू और बार कहते हैं। यह थकाऊ स्क्रिप्ट के लिए उपयोगी है।


3

आप कुछ इस तरह की कोशिश कर सकते हैं:

if( ARGV.include( '-f' ) )
  file = ARGV[ARGV.indexof( '-f' ) + 1 )]
  ARGV.delete('-f')
  ARGV.delete(file)
end

3

क्या आपने विचाराधीन थोर पर विचार किया है? मुझे लगता है कि यह ऑप्टपर्स की तुलना में बहुत क्लीनर है। यदि आपके पास पहले से कोई स्क्रिप्ट लिखी गई है, तो इसे प्रारूपित करने या इसे थोर के लिए रिफैक्टर करने के लिए कुछ और काम हो सकते हैं, लेकिन यह हैंडलिंग विकल्पों को बहुत सरल बनाता है।

यहाँ README से उदाहरण स्निपेट दिया गया है:

class MyApp < Thor                                                # [1]
  map "-L" => :list                                               # [2]

  desc "install APP_NAME", "install one of the available apps"    # [3]
  method_options :force => :boolean, :alias => :optional          # [4]
  def install(name)
    user_alias = options[:alias]
    if options.force?
      # do something
    end
    # ... other code ...
  end

  desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
  def list(search = "")
    # list everything
  end
end

थोर स्वचालित रूप से इस तरह के रूप में कमांड मैप्स

app install myname --force

यह परिवर्तित हो जाता है:

MyApp.new.install("myname")
# with {'force' => true} as options hash
  1. एक विकल्प मैपर में एक कक्षा चालू करने के लिए थोर से प्रवेश करें
  2. विशिष्ट तरीकों के लिए अतिरिक्त गैर-वैध पहचानकर्ताओं को मैप करें। इस स्थिति में, -L को: सूची में बदलें
  3. नीचे दी गई विधि का तुरंत वर्णन करें। पहला पैरामीटर उपयोग जानकारी है, और दूसरा पैरामीटर विवरण है।
  4. कोई अतिरिक्त विकल्प प्रदान करें। ये - और - परम से मार्श किया जाएगा। इस स्थिति में, - aforce और a -f विकल्प जोड़ा जाता है।

मुझे कमांड-मैपिंग की बात पसंद है, क्योंकि उपखंडों के झुंड के साथ एक एकल बाइनरी कुछ ऐसा है जो मैं अक्सर करता हूं। फिर भी, हालांकि आप 'प्रकाश' से एक तरीके से चले गए हैं। क्या आप उसी कार्यक्षमता को व्यक्त करने का एक सरल तरीका खोज सकते हैं? क्या होगा यदि आपको --helpआउटपुट प्रिंट करने की आवश्यकता नहीं है ? क्या होगा यदि "हेड myprogram.rb" मदद आउटपुट था?
cjs

3

यहाँ मेरा पसंदीदा त्वरित और गंदा विकल्प पार्सर है:

case ARGV.join
when /-h/
  puts "help message"
  exit
when /-opt1/
  puts "running opt1"
end

विकल्प नियमित अभिव्यक्ति हैं, इसलिए "-h" भी "--help" से मेल खाएगा।

पढ़ने योग्य, याद रखने में आसान, कोई बाहरी पुस्तकालय और न्यूनतम कोड नहीं।


हाँ यह होगा। यदि यह एक समस्या है, तो आप अधिक रेगेक्स जोड़ सकते हैं, उदाहरण के लिए/-h(\b|elp)
एडवर्डटेक

2

ट्रोलोप काफी सस्ता है।


वह होगा, < trollop.rubyforge.org >। मुझे यह पसंद है, मुझे लगता है, हालांकि मैं वास्तव में एक पुस्तकालय की तलाश में नहीं था।
cjs

सच है, यह एक पुस्तकालय है। हालांकि, <800 LOC पर, यह उस पर एक बहुत ही नगण्य है। gitorious.org/trollop/mainline/blobs/master/lib/trollop.rb
g33kz0r

1
मैं सोच रहा था कि शायद 30-50 लाइनें अच्छी होंगी, अगर मैं "लाइब्रेरी" का उपयोग करने के लिए इतनी दूर जा रहा था। लेकिन फिर, मुझे लगता है कि एक बार जब आप कोड से भरा एक अलग फ़ाइल के रूप में मिल गया है, लाइन की तुलना में एपीआई डिजाइन अधिक महत्वपूर्ण है। फिर भी, मुझे यकीन नहीं है कि मैं इसे एकबारगी स्क्रिप्ट में शामिल करना चाहूंगा जिसे मैं बस एक यादृच्छिक प्रणाली पर बिन निर्देशिका में बंद करना चाहता हूं।
cjs

1
आप इसे पीछे की ओर ले गए हैं: बिंदु एक अधिक जटिल तैनाती रणनीति होने से बचने के लिए है।
cjs

1
आप काफी गलत हैं, क्योंकि आप सवाल पूछने वाले व्यक्ति की जरूरतों और इरादों को पूरी तरह से गलत बता रहे हैं (या अनदेखा कर रहे हैं)। मेरा सुझाव है कि आप प्रश्न को ध्यान से पढ़ें, विशेष रूप से अंतिम दो बिंदु।
cjs

2

यदि आप रत्नों के उपयोग के बिना कुंजी / मान कमांड के लिए एक साधारण कमांड लाइन पार्सर चाहते हैं:

लेकिन यह केवल तभी काम करता है जब आपके पास हमेशा कुंजी / मूल्य जोड़े हों।

# example
# script.rb -u username -p mypass

# check if there are even set of params given
if ARGV.count.odd? 
    puts 'invalid number of arguments'
    exit 1
end

# holds key/value pair of cl params {key1 => value1, key2 => valye2, ...}
opts = {} 

(ARGV.count/2).times do |i|
    k,v = ARGV.shift(2)
    opts[k] = v # create k/v pair
end

# set defaults if no params are given
opts['-u'] ||= 'root'

# example use of opts
puts "username:#{opts['-u']} password:#{opts['-p']}"

यदि आपको किसी ऐसे चेक की आवश्यकता नहीं है, जिसका आप उपयोग कर सकते हैं:

opts = {} 

(ARGV.count/2).times do |i|
    k,v = ARGV.shift(2)
    opts[k] = v # create k/v pair
end

2

यहाँ कोड स्निपेट है जिसका उपयोग मैं अपनी अधिकांश लिपियों में करता हूँ:

arghash = Hash.new.tap { |h| # Parse ARGV into a hash
    i = -1                      
    ARGV.map{  |s| /(-[a-zA-Z_-])?([^=]+)?(=)?(.+)?/m.match(s).to_a }
     .each{ |(_,a,b,c,d)| h[ a ? "#{a}#{b}#{c}" : (i+=1) ] =
                             (a ? (c ? "#{d}" : true) : "#{b}#{c}#{d}") 
          }
    [[:argc,Proc.new  {|| h.count{|(k,_)| !k.is_a?(String)}}],
     [:switches, Proc.new {|| h.keys.select{|k| k[0] == '-' }}]
    ].each{|(n,p)| h.define_singleton_method(n,&p) }
}

मुझे अपनी त्वरित और गंदी स्क्रिप्ट में अतिरिक्त फ़ाइलों की आवश्यकता से भी नफरत है। मेरा समाधान बहुत करीब है जो आप पूछ रहे हैं। मैं अपनी किसी भी स्क्रिप्ट के शीर्ष पर कोड की 10 पंक्ति स्निपेट पेस्ट करता हूं जो कमांड लाइन को पार्स करता है और पोजिशनल आर्ग्स को चिपकाता है और एक हैश ऑब्जेक्ट में स्विच करता है (आमतौर पर किसी ऑब्जेक्ट को असाइन किया जाता है जिसे मैंने नीचे दिए उदाहरणों में arghash नाम दिया है )।

यहां एक उदाहरण कमांड लाइन है जिसे आप पार्स करना चाहते हैं ...

./myexampleprog.rb -s -x=15 --longswitch arg1 --longswitch2=val1 arg2

जो इस तरह हाश बन जाता।

 { 
   '-s' => true, 
   '-x=' => '15', 
   '--longswitch' => true, 
   '--longswitch2=' => 'val1', 
   0 => 'arg1', 
   1 => 'arg2'
 }

इसके अलावा, दो सुविधा विधियाँ हाश में जोड़ी जाती हैं:

  • argc() गैर-स्विच तर्कों की गिनती लौटाएगा।
  • switches() मौजूद सरणी के लिए कुंजियों वाली एक सरणी लौटाएगा

यह कुछ त्वरित और गंदे सामान की अनुमति देने के लिए है ...

  • मान्य (स्विच arghash.argc == 2) की परवाह किए बिना मुझे स्थिति संबंधी तर्कों की सही संख्या मिल गई है ( )
  • स्थिति सापेक्ष तर्कों के साथ उनकी स्थिति के अनुसार स्थितिगत दलीलें एक्सेस करें, चाहे वे स्थितीय तर्कों के साथ या पहले से अटके हुए हों (जैसे arghash[1]हमेशा दूसरा गैर-स्विच तर्क मिलता है)।
  • कमांड लाइन में सपोर्ट-वैल्यू-असाइन किए गए स्विच जैसे "--max = 15" जिसे एक्सेस किया जा सकता है, arghash['--max=']जिसके द्वारा '15' के मान को उदाहरण कमांड लाइन दिया जाता है।
  • कमांड लाइन में एक स्विच की उपस्थिति या अनुपस्थिति के लिए परीक्षण एक बहुत ही सरल संकेतन का उपयोग करके किया जाता है जैसे कि arghash['-s']अगर यह मौजूद है और शून्य है तो इसके सही होने का मूल्यांकन करता है।
  • स्विच की उपस्थिति के लिए टेस्ट करें या सेट ऑपरेशन जैसे स्विच का विकल्प

    puts USAGETEXT if !(%w(-h --help) & arghash.switches()).empty?

  • सेट संचालन जैसे कि अमान्य स्विचेस के उपयोग की पहचान करें

    puts "Invalid switch found!" if !(arghash.switches - %w(-valid1 -valid2)).empty?

  • Hash.merge()नीचे दिए गए उदाहरण के रूप में एक साधारण उदाहरण का उपयोग करते हुए लापता तर्कों के लिए डिफ़ॉल्ट मान निर्दिष्ट करें- जो किसी मान के लिए भरता है = यदि कोई सेट नहीं किया गया था और यदि कोई पारित नहीं हुआ था, तो 4 वें स्थितीय तर्क जोड़ता है।

    with_defaults = {'-max=' => 20, 3 => 'default.txt'}.merge(arghash)


(मैंने कोड स्वरूपण को बेहतर बनाने के लिए इसे संपादित किया है, मुख्य रूप से ब्लॉक और नियंत्रण संरचना को स्पष्ट करने के लिए संरेखण का उपयोग करना, जो मुझे लगता है कि विशेष रूप से विराम चिह्न के साथ इस घने कुछ में महत्वपूर्ण है। लेकिन यदि आप नए स्वरूपण से नफरत करते हैं, तो कृपया स्वतंत्र महसूस करें। संपादित करने के लिए।)
cjs

यह बहुत अच्छा है, अगर पढ़ने के लिए दुनिया की सबसे आसान चीज नहीं है। मुझे यह पसंद है कि यह उस बदलते तर्क "वाक्यविन्यास" को भी प्रदर्शित करता है (यहाँ, स्थितीय आर्गन्स के बाद विकल्पों को अनुमति देना और उपयोग करने के अलावा विकल्प तर्कों को अस्वीकार करना =) आपके द्वारा आवश्यक कोड में अंतर ला सकता है।
cjs

सुधार के लिए धन्यवाद। यह निश्चित रूप से अस्पष्ट है और कोई स्पष्टता के लिए कोड की लंबाई को आसानी से व्यापार कर सकता है। अब जब मैं इस कोड पर भरोसा करता हूं, कम या ज्यादा, मैं इसे एक मणि की तरह मानता हूं और मैं कभी यह पता लगाने की कोशिश नहीं करता कि यह कवर के तहत क्या कर रहा है (इसलिए स्पष्टता अब महत्वपूर्ण नहीं है कि मुझे विश्वास है)।
डेविड फोस्टर

1

यह स्वीकृत उत्तर के समान है, लेकिन इसका उपयोग ARGV.delete_ifमैं अपने साधारण पार्सर में करता हूं । एकमात्र वास्तविक अंतर यह है कि तर्कों के साथ विकल्प एक साथ होने चाहिए (जैसे -l=file)।

def usage
  "usage: #{File.basename($0)}: [-l=<logfile>] [-q] file ..."
end

$quiet = false
$logfile = nil

ARGV.delete_if do |cur|
  next false if cur[0] != '-'
  case cur
  when '-q'
    $quiet = true
  when /^-l=(.+)$/
    $logfile = $1
  else
    $stderr.puts "Unknown option: #{cur}"
    $stderr.puts usage
    exit 1
  end
end

puts "quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}"

0

जाहिरा तौर पर @WilliamMorgan और मुझे एक जैसा लगता है। मैंने अभी कल रात जीथूब को जारी किया, जो अब मैं देख रहा हूँ, ट्रोलोप (नाम कैसे?) के लिए एक समान पुस्तकालय है, जीथब पर OptionParser की खोज करने के बाद, स्विच देखें

कुछ अंतर हैं, लेकिन दर्शन समान है। एक स्पष्ट अंतर यह है कि स्विचेस OptionParser पर निर्भर है।


0

मैं अपना खुद का विकल्प पार्सर रत्न विकसित कर रहा हूं जिसे Acclaim कहा जाता है

मैंने इसे लिखा था क्योंकि मैं गिट-स्टाइल कमांड लाइन इंटरफेस बनाना चाहता था और प्रत्येक कमांड की कार्यक्षमता को अलग-अलग वर्गों में सफाई से अलग करने में सक्षम था, लेकिन इसका उपयोग पूरे कमांड ढांचे के बिना भी किया जा सकता है:

(options = []) << Acclaim::Option.new(:verbose, '-v', '--verbose')
values = Acclaim::Option::Parser.new(ARGV, options).parse!
puts 'Verbose.' if values.verbose?

अभी तक कोई स्थिर रिलीज नहीं है, लेकिन मैंने पहले से ही कुछ सुविधाओं को लागू किया है:

  • कस्टम विकल्प पार्सर
  • विकल्प के तर्कों का लचीला पार्सिंग जो न्यूनतम और वैकल्पिक दोनों के लिए अनुमति देता है
  • कई विकल्प शैलियों के लिए समर्थन
  • एक ही विकल्प के कई उदाहरणों को बदलना, जोड़ना या उठाना
  • कस्टम विकल्प संचालकों
  • कस्टम प्रकार हैंडलर
  • सामान्य मानक पुस्तकालय कक्षाओं के लिए पूर्वनिर्धारित हैंडलर

आज्ञाओं पर बहुत अधिक जोर दिया गया है, इसलिए यह साधारण कमांड लाइन पार्सिंग के लिए थोड़ा भारी हो सकता है, लेकिन यह अच्छी तरह से काम करता है और मैं अपनी सभी परियोजनाओं पर इसका उपयोग कर रहा हूं। यदि आप कमांड इंटरफ़ेस पहलू में रुचि रखते हैं, तो प्रोजेक्ट के GitHub पृष्ठ को देखें अधिक जानकारी और उदाहरणों के लिए को देखें।


1
मैं अत्यधिक प्राप्ति की सलाह देता हूं। इसका उपयोग करना आसान है और आपके पास आवश्यक सभी विकल्प हैं।
बोहर्सनियोर जूल

0

मान लीजिए कि एक कमांड में अधिकांश कार्रवाई होती है और इस तरह के विकल्पों की मनमानी होती है:

cmd.rb
cmd.rb action
cmd.rb action -a -b ...
cmd.rb action -ab ...

सत्यापन के बिना पार्सिंग इस तरह हो सकता है:

ACTION = ARGV.shift
OPTIONS = ARGV.join.tr('-', '')

if ACTION == '***'
  ...
  if OPTIONS.include? '*'
    ...
  end
  ...
end

0

https://github.com/soveran/clap

other_args = Clap.run ARGV,
  "-s" => lambda { |s| switch = s },
  "-o" => lambda { other = true }

46LOC (1.0.0 पर), बाहरी विकल्प पार्सर पर कोई निर्भरता नहीं। काम हो जाता है। शायद दूसरों के रूप में पूर्ण रूप से चित्रित नहीं किया गया है, लेकिन यह 46LOC है।

यदि आप कोड की जांच करते हैं तो आप अंतर्निहित तकनीक को आसानी से डुप्लिकेट कर सकते हैं - लैम्बदास को असाइन करें और उचित संख्या में आर्ग्स को ध्वज का पालन करने के लिए सुनिश्चित करने के लिए यदि आप वास्तव में बाहरी लाइब्रेरी नहीं चाहते हैं, तो इसका उपयोग करें।

सरल। सस्ते।


संपादित करें : जैसा कि मुझे लगता है कि आप एक उचित कमांड लाइन पार्सर बनाने के लिए इसे स्क्रिप्ट में कॉपी / पेस्ट कर सकते हैं, अंतर्निहित अवधारणा उबला हुआ है। यह निश्चित रूप से ऐसा कुछ नहीं है जिसे मैं स्मृति के लिए प्रतिबद्ध करूंगा, लेकिन एक सस्ते पार्सर के रूप में लैम्ब्डा एरिटी का उपयोग करना एक उपन्यास विचार है:

flag = false
option = nil
opts = {
  "--flag" => ->() { flag = true },
  "--option" => ->(v) { option = v }
}

argv = ARGV
args = []

while argv.any?
  item = argv.shift
  flag = opts[item]

  if flag
    raise ArgumentError if argv.size < arity
    flag.call(*argv.shift(arity))
  else
    args << item
  end
end

# ...do stuff...

कृपया प्रश्न के अंत में बिंदु 1 पढ़ें। यदि आप यहाँ अपने उत्तर में सभी आवश्यक कोड नहीं लिख सकते हैं, तो यह प्रश्न का उत्तर नहीं है।
cjs

अच्छी बात! मुझे लगता है कि उस समय मैंने यह मान लिया था कि लिबर काफी छोटा था कि आप पूरी चीज़ को कॉपी कर सकते हैं / पेस्ट कर सकते हैं जो आप किसी बाहरी निर्भरता की आवश्यकता पर काम कर रहे हैं, लेकिन यह निश्चित रूप से एक साफ-सुथरा लाइनर नहीं है जिसे मैं याद रखूंगा। अपनी बात पूरी करने के लिए # २। मुझे लगता है कि अंतर्निहित अवधारणा उपन्यास और पर्याप्त शांत है कि मैंने आगे बढ़कर एक उबला हुआ संस्करण बनाया, जो आपके प्रश्न का उचित रूप से जवाब देता है।
बेन अलावी

-1

मैं अपना सरल विकल्प पार्सर साझा करने जा रहा हूं जो मैं कुछ समय के लिए काम कर रहा हूं। यह केवल 74 लाइनों का कोड है, और यह Git के आंतरिक विकल्प पार्सर की मूल बातें करता है। मैंने OptionParser को प्रेरणा के रूप में लिया, और Git का भी।

https://gist.github.com/felipec/6772110

यह इस तरह दिख रहा है:

opts = ParseOpt.new
opts.usage = "git foo"

opts.on("b", "bool", help: "Boolean") do |v|
 $bool = v
end

opts.on("s", "string", help: "String") do |v|
 $str = v
end

opts.on("n", "number", help: "Number") do |v|
 $num = v.to_i
end

opts.parse

आपने कोड की जाँच भी नहीं की। मैंने पार्सिंग कोड निकालकर एक और जवाब दिया है।
फेलिपक

मुझे यह बताने की जरूरत नहीं थी कि आपने कहा था कि यह need४ लाइनें लंबी थीं। हालाँकि, मैंने अभी इसे देखा है और यह अभी भी आवश्यकता के पहले वाक्य का उल्लंघन करता है 2. (यह प्रतिक्रिया स्टैक ओवरफ्लो सम्मेलन का भी उल्लंघन करती है कि आपको ऑफ-साइट लिंक देने के बजाय कोड को अपने उत्तर में शामिल करना चाहिए।)
cjs

-1

EasyOptions को किसी भी विकल्प को पार्सिंग कोड की आवश्यकता नहीं है। बस सहायता पाठ लिखें, आवश्यकता है, किया।

## Options:
##   -i, --interactive  Interactive mode
##   -q, --quiet        Silent mode

require 'easyoptions'
unless EasyOptions.options[:quiet]
    puts 'Interactive mode enabled' if EasyOptions.options[:interactive]
    EasyOptions.arguments.each { |item| puts "Argument: #{item}" }
end

EasyOptions बयानों की आवश्यकता के बिना एक एकल रूबी फ़ाइल है, और सभी को याद रखने के लिए कोई पार्सिंग कोड नहीं है। ऐसा लगता है कि आप इसके बजाय कुछ ऐसा एम्बेड करना चाहते हैं जो याद रखने के लिए पर्याप्त शक्तिशाली हो।
रेनाटो सिल्वा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.