रूबी में उपयोग के संकेत देने के लिए मुझे आदेश का नाम कैसे मिल सकता है?


83

मैंने कुछ समय पहले एक अच्छी छोटी रूबी स्क्रिप्ट लिखी थी जो मुझे पसंद है। मैं उचित संख्या में तर्कों की जाँच करके इसकी मजबूती में सुधार करना चाहता हूँ:

if ARGV.length != 2 then
  puts "Usage: <command> arg1 arg2"
end

बेशक वह स्यूडोकोड है। वैसे भी, C या C ++ में, मैं argv[0]उस नाम को प्राप्त करने के लिए उपयोग कर सकता हूं जो उपयोगकर्ता मेरी आज्ञा के लिए उपयोग करता था, चाहे वे इसे पसंद करें ./myScript.rbया myScript.rbया /usr/local/bin/myScript.rb। रूबी में, मुझे पता है कि ARGV[0]यह पहला सच्चा तर्क है, और ARGVइसमें कमांड का नाम नहीं है। क्या कोई ऐसा तरीका है जिससे मुझे यह मिल सकता है?

जवाबों:


149

रूबी ने हमें बुलाया स्क्रिप्ट का नाम देने के तीन तरीके हैं:

#!/usr/bin/env ruby

puts "$0            : #{$0}"
puts "__FILE__      : #{__FILE__}"
puts "$PROGRAM_NAME : #{$PROGRAM_NAME}"

उस कोड को "test.rb" के रूप में सहेजना और इसे कुछ तरीके से कॉल करना दिखाता है कि स्क्रिप्ट को वह नाम प्राप्त होता है जैसा कि इसे OS द्वारा दिया गया था। एक स्क्रिप्ट केवल यह जानती है कि OS इसे क्या बताता है:

$ ./test.rb 
$0            : ./test.rb
__FILE__      : ./test.rb
$PROGRAM_NAME : ./test.rb

$ ~/Desktop/test.rb 
$0            : /Users/ttm/Desktop/test.rb
__FILE__      : /Users/ttm/Desktop/test.rb
$PROGRAM_NAME : /Users/ttm/Desktop/test.rb

$ /Users/ttm/Desktop/test.rb 
$0            : /Users/ttm/Desktop/test.rb
__FILE__      : /Users/ttm/Desktop/test.rb
$PROGRAM_NAME : /Users/ttm/Desktop/test.rb

~दूसरे उदाहरण में $ HOME के ​​लिए शॉर्टकट का उपयोग करके इसे कॉल करना ओएस को विस्तारित पथ के साथ प्रतिस्थापित करना दिखाता है, जो तीसरे उदाहरण में है। सभी मामलों में यह है कि ओएस में क्या पारित हुआ।

हार्ड और सॉफ्ट दोनों लिंक का उपयोग करके फाइल को लिंक करना लगातार व्यवहार दिखाता है। मैंने test1.rb के लिए एक हार्ड लिंक और test2.rb के लिए एक सॉफ्ट लिंक बनाया:

$ ./test1.rb 
$0            : ./test1.rb
__FILE__      : ./test1.rb
$PROGRAM_NAME : ./test1.rb

$ ./test2.rb 
$0            : ./test2.rb
__FILE__      : ./test2.rb
$PROGRAM_NAME : ./test2.rb

ruby test.rbस्क्रिप्ट नाम पर किसी भी विविधता के साथ लॉन्च करना लगातार परिणाम देता है।

यदि आप केवल नामांकित फ़ाइल नाम चाहते हैं, तो आप फ़ाइल के basenameतरीके का एक चर के साथ उपयोग कर सकते हैं या सीमांकक पर विभाजित कर सकते हैं और अंतिम तत्व ले सकते हैं।

$0और __FILE__कुछ मामूली अंतर हैं लेकिन एकल लिपियों के लिए वे समकक्ष हैं।

puts File.basename($0)

विधियों का उपयोग File.basename, File.extnameऔर File.dirnameसुइट करने के लिए कुछ लाभ हैं । basenameएक वैकल्पिक पैरामीटर लेता है, जो स्ट्रिप का विस्तार है, इसलिए यदि आपको एक्सटेंशन के बिना बस बेसन की आवश्यकता है

File.basename($0, File.extname($0)) 

यह पहिया को रोकने के बिना या चर-लंबाई या लापता एक्सटेंशन या गलत तरीके से ट्रंकिंग एक्सटेंशन चेन की संभावना से निपटने के .rb.txtलिए करता है " ":

ruby-1.9.2-p136 :004 > filename = '/path/to/file/name.ext'
 => "/path/to/file/name.ext" 
ruby-1.9.2-p136 :005 > File.basename(filename, File.extname(filename))
 => "name" 
ruby-1.9.2-p136 :006 > filename = '/path/to/file/name.ext' << '.txt'
 => "/path/to/file/name.ext.txt" 
ruby-1.9.2-p136 :007 > File.basename(filename, File.extname(filename))
 => "name.ext" 

बहुत पूर्ण उत्तर के लिए धन्यवाद!
adam_0

2
ध्यान दें कि यदि आप किसी लिपि में सुझाए गए विकल्पों का उपयोग करते हैं तो or आवश्यकता ’या is requ_relative’ का उपयोग करते हुए अलग-अलग व्यवहार होता है। $ 0 और $ PROGRAM_NAME के ​​उपयोग से कॉलिंग स्क्रिप्ट का नाम वापस आ जाता है। आसपास के डबल अंडरबार के साथ फ़ाइल विकल्प का उपयोग करना (आप टिप्पणी में कैसे प्रारूपित कर सकते हैं?) में शामिल स्क्रिप्ट का नाम दिया गया है।
mike663

18

यह उत्तर थोड़ा देर से आ सकता है, लेकिन मेरे पास एक ही मुद्दा था और स्वीकृत उत्तर मुझे बहुत संतोषजनक नहीं लगा, इसलिए मैंने थोड़ी और जांच की।

मुझे जो परेशान करता था वह यह था कि उपयोगकर्ता ने जो टाइप किया था, उसके बारे में सही जानकारी नहीं थी $0या $PROGRAM_NAMEनहीं । यदि मेरी रूबी स्क्रिप्ट एक PATH फ़ोल्डर में थी और उपयोगकर्ता ने निष्पादन योग्य नाम (जैसे या किसी भी पथ परिभाषा के बिना ) दर्ज किया, तो यह हमेशा कुल पथ तक विस्तारित होगा।./script/bin/script

मुझे लगा कि यह रूबी की कमी है, इसलिए मैंने पायथन के साथ भी यही कोशिश की और वहां पर अपने तीर्थयात्रियों के लिए, यह अलग नहीं था।

एक दोस्त ने मुझे एक हैक सुझाव देखने के लिए real thingमें /proc/self/cmdlineहै, और परिणाम था: [ruby, /home/danyel/bin/myscript, arg1, arg2...](शून्य-चार से विभाजित)। यहाँ खलनायक है, execve(1)जो एक दुभाषिया के पास जाने पर कुल पथ का विस्तार करता है।

उदाहरण सी कार्यक्रम:

#include <stdlib.h>
#include <unistd.h>

extern char** environ;
int main() {
  char ** arr = malloc(10 * sizeof(char*));
  arr[0] = "myscript";
  arr[1] = "-h";
  arr[2] = NULL;
  execve("/home/danyel/bin/myscript", arr, environ);
}

आउटपुट: :Usage: / home / danyel / bin / myscript फ़ाइल ...

यह साबित करने के लिए कि यह वास्तव में एक execveचीज है और मारपीट से नहीं, हम एक डमी दुभाषिया बना सकते हैं जो कुछ भी नहीं करता है लेकिन इसके लिए दिए गए तर्कों को प्रिंट करता है:

// interpreter.c
int main(int argc, const char ** argv) {
  while(*argv)
    printf("%s\n", *(argv++));
}

हम इसे संकलित करते हैं और इसे एक पथ फ़ोल्डर में डालते हैं (या शबंग के बाद पूरा रास्ता डालते हैं) और एक डमी स्क्रिप्ट बनाते हैं ~/bin/myscript/

#!/usr/bin/env interpreter
Hi there!

अब, हमारे main.c में:

#include <stdlib.h>

extern char** environ;
int main() {
  char ** arr = malloc(10 * sizeof(char*));
  arr[0] = "This will be totally ignored by execve.";
  arr[1] = "-v";
  arr[2] = "/var/log/apache2.log";
  arr[3] = NULL;
  execve("/home/danyel/bin/myscript", arr, environ);
}

संकलन और चल रहा है ./main: दुभाषिया / घर / डैनियल / बिन / myscript -v /var/log/apache2.log

इस सबसे अधिक संभावना के पीछे कारण यह है कि यदि स्क्रिप्ट आपके पेट में है और पूर्ण पथ प्रदान नहीं किया गया है, तो दुभाषिया इसे एक No such fileत्रुटि के रूप में पहचानेगा , जो यह करता है यदि आप करते हैं: ruby myrubyscript --options arg1और आप उस स्क्रिप्ट के साथ फ़ोल्डर में नहीं हैं ।


3

उस फ़ाइल नाम का उपयोग करें $0या $PROGRAM_NAMEप्राप्त करें जिसे वर्तमान में निष्पादित किया जा रहा है।


इससे मुझे पूरा रास्ता मिल गया। मैं जानना चाहूंगा कि उपयोगकर्ता ने क्या दर्ज किया। उदाहरण के लिए, अगर मेरे पास है /usr/local/bin/myScriptऔर मेरे पास /usr/local/binहै $PATH, और मैं सिर्फ टाइप करता myScriptहूं, तो मुझे मिलता /usr/local/bin/myScriptहै$0
adam_0

2
कैसे के बारे में $0.split("/").last?
पियरोटलफौ

7
मैं सिर्फ प्रोग्राम का नाम नहीं पूछ रहा हूं, मेरा मतलब यह है कि मुझे वही चाहिए जो उपयोगकर्ता प्रोग्राम को चलाने के लिए टाइप करता है। यदि वे टाइप करते हैं ./myScript, तो मुझे एक चर चाहिए जो मुझे देता है ./myScript। अगर वे टाइप करते हैं /usr/bin/local/myScript, तो मैं बिल्कुल यही चाहता हूं। आदि
adam_0

3

यह आपके प्रश्न का काफी जवाब नहीं है, लेकिन ऐसा लगता है कि आप एक पहिया को फिर से संगठित कर रहे हैं। को देखो optparse पुस्तकालय। यह आपको कमांड लाइन स्विच, तर्क आदि को परिभाषित करने देता है, और यह आपके लिए सभी भारी उठाने का काम करेगा।


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