रूबी से शेल कमांड कैसे कॉल करें


1077

मैं रूबी प्रोग्राम के अंदर से शेल कमांड कैसे कह सकता हूं? फिर मैं रूबी में इन कमांड से आउटपुट कैसे प्राप्त करूं?


3
हालांकि यह प्रश्न उपयोगी है, यह अच्छी तरह से नहीं पूछा गया है। रूबी के पास उप-गोले को कॉल करने के कई तरीके हैं जो कि अच्छी तरह से प्रलेखित हैं और आसानी से कर्नेल और ओपन 3 प्रलेखन को पढ़कर और एसओ के साथ यहां खोज कर पाए जाते हैं ।
टिन मैन

1
अफसोस की बात है कि यह विषय काफी जटिल है। Open3( डॉक्स ) अधिकांश स्थितियों के लिए सबसे अच्छा विकल्प है, IMO, लेकिन रूबी के पुराने संस्करणों पर, यह संशोधित PATH( Bugs.ruby-lang.org/issues/8004 ) का सम्मान नहीं करेगा , और आप कैसे आर्ग ( विशेष रूप से ) के आधार पर , यदि आप गैर-कीवर्ड के साथ opts हैश का उपयोग करते हैं), तो यह टूट सकता है। लेकिन, यदि आप उन स्थितियों को मारते हैं, तो आप कुछ बहुत उन्नत कर रहे हैं और आप यह जान सकते हैं कि कार्यान्वयन को पढ़कर क्या करना है Open3
जोशुआ गाल

3
मुझे आश्चर्य है कि किसी ने भी उल्लेख नहीं किया Shellwords.escape( डॉक्टर )। आप उपयोगकर्ता-इनपुट को सीधे शेल कमांड में सम्मिलित नहीं करना चाहते हैं - इसे पहले से बचो! कमांड इंजेक्शन भी देखें ।
केल्विन

जवाबों:


1319

यह स्पष्टीकरण मेरे एक दोस्त की टिप्पणी वाली रूबी स्क्रिप्ट पर आधारित है । यदि आप स्क्रिप्ट में सुधार करना चाहते हैं, तो लिंक पर इसे अद्यतन करने के लिए स्वतंत्र महसूस करें।

सबसे पहले, ध्यान दें कि जब रूबी एक शेल को कॉल करती है, तो यह आम तौर पर कॉल करता है /bin/sh, न कि बैश। कुछ बैश सिंटैक्स /bin/shसभी सिस्टम पर समर्थित नहीं हैं।

यहां शेल स्क्रिप्ट निष्पादित करने के तरीके दिए गए हैं:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` , जिसे बैकटिक्स कहा जाता है - `cmd`

    यह बाश, PHP और पर्ल सहित कई अन्य भाषाओं की तरह है।

    शेल कमांड का परिणाम (अर्थात मानक आउटपुट) लौटाता है।

    डॉक्स: http://ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. अंतर्निहित सिंटैक्स, %x( cmd )

    बाद xचरित्र परिसीमक है, जो किसी भी चरित्र हो सकता है। यदि सीमांकक पात्रों में से एक है (, [, {, या <, शाब्दिक वर्णों का मिलान समापन सीमांकक अप करने के लिए, होते नेस्टेड सीमांकक जोड़े के खाते में ले रही है। अन्य सभी सीमांकक के लिए, शाब्दिक सीमांकक वर्ण की अगली घटना तक वर्णों को समाहित करता है। स्ट्रिंग प्रक्षेप #{ ... }की अनुमति है।

    बैकटिक्स की तरह शेल कमांड का परिणाम (यानी मानक आउटपुट) लौटाता है।

    डॉक्स: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings

    value = %x( echo 'hi' )
    value = %x[ #{cmd} ]
    
  3. Kernel#system

    दिए गए कमांड को सब्स्क्राइब में निष्पादित करता है।

    trueयदि आदेश मिला और सफलतापूर्वक चला, तो वापस लौटाता है false

    डॉक्स: http://ruby-doc.org/core/Kernel.html#method-i-system

    wasGood = system( "echo 'hi'" )
    wasGood = system( cmd )
    
  4. Kernel#exec

    दी गई बाहरी कमांड को चलाकर वर्तमान प्रक्रिया को बदल देता है।

    कोई नहीं लौटाता है, वर्तमान प्रक्रिया को प्रतिस्थापित किया जाता है और कभी जारी नहीं रहता है।

    डॉक्स: http://ruby-doc.org/core/Kernel.html#method-i-exec

    exec( "echo 'hi'" )
    exec( cmd ) # Note: this will never be reached because of the line above
    

यहां कुछ अतिरिक्त सलाह दी गई है: $?जो कि जैसा है $CHILD_STATUS, यदि आप बैकटिक्स का उपयोग करते हैं, तो अंतिम सिस्टम निष्पादित कमांड की स्थिति तक पहुंचता है, system()या %x{}। आप तब exitstatusऔर pidगुणों तक पहुँच सकते हैं:

$?.exitstatus

अधिक पढ़ने के लिए देखें:


4
मुझे उत्पादन सर्वर पर अपने निष्पादन योग्य के आउटपुट को लॉग इन करना होगा लेकिन कोई रास्ता नहीं मिला। मैंने पुट्स #{cmd}और logger.info ( #{cmd}) का उपयोग किया । क्या उत्पादन पर उनके आउटपुट को लॉग करने का कोई तरीका है?
ओमर असलम

5
और IO # popen () और Open3 # popen3 ()। mentalized.net/journal/2010/03/08/…
hughdbrown

6
पूर्णता के लिए (जैसा कि मैंने पहले सोचा था कि यह एक रूबी कमांड भी होगा): रेक में होता है जो "सिस्टम कमांड को रन करता है cmd। यदि कई तर्क दिए जाते हैं तो कमांड शेल के साथ नहीं चलाया जाता है (कर्नेल के समान शब्दार्थ: निष्पादन और कर्नेल :: सिस्टम) "।
sschuberth

40
बैकटिक्स डिफ़ॉल्ट रूप से STDERR पर कब्जा नहीं करता है। यदि आप कब्जा करना चाहते हैं, तो कमांड में `2> & 1` संलग्न करें
आंद्रेई बोटालोव

14
मुझे लगता है कि यह उत्तर थोड़ा सुधरा होगा अगर यह कहा जाए कि बैकटिक्स और% x ने दिए गए कमांड के "परिणाम" के बजाय "आउटपुट" लौटा दिया। बाद की स्थिति से बाहर निकलने की स्थिति में गलती हो सकती है। या क्या वह सिर्फ मैं हूं?
skagedal

275

24
वाह हाहा। बहुत उपयोगी होने पर भी इस तथ्य का अस्तित्व दुर्भाग्यपूर्ण है
जोश बोधा

एक साइड नोट के रूप में, मुझे कई अलग-अलग जगहों पर पाई जाने वाली स्पॉन () विधि मिलती है (उदाहरण के लिए Kernelऔर Processसबसे बहुमुखी होना। यह कम या ज्यादा समान है PTY.spawn(), लेकिन अधिक सामान्य है।
Smar

160

जिस तरह से मैं ऐसा करना पसंद करता हूं वह %xशाब्दिक का उपयोग कर रहा है , जो एक कमांड में उद्धरण का उपयोग करना आसान (और पठनीय!) बनाता है, जैसे:

directorylist = %x[find . -name '*test.rb' | sort]

इस मामले में, वर्तमान निर्देशिका के तहत सभी परीक्षण फ़ाइलों के साथ फ़ाइल सूची पॉप्युलेट होगी, जिसे आप अपेक्षित रूप से संसाधित कर सकते हैं:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

4
क्या आपके पास %x[ cmd ]कोई सरणी है?
x-यूरी

2
ऊपर मेरे लिए काम नहीं करता है। `` <मुख्य> ': अपरिभाषित विधि आपके लिए each' for :String (NoMethodError) कैसे काम करती है? मैं उपयोग ruby -v ruby 1.9.3p484 (2013-11-22 revision 43786) [i686-linux]कर रहा हूं क्या आपको यकीन है कि एक सरणी कमांड से लौटा दी गई है ताकि लूप वास्तव में काम करेगा?
नासिर

% एक्स [cmd] .split ( "\ n"), हालांकि :) एक सूची प्रदान करेगा
इयान एलिस

65

यहाँ रूबी में शेल स्क्रिप्ट चलाने के बारे में मेरी राय में सबसे अच्छा लेख है: " रूबी में शेल कमांड चलाने के 6 तरीके "।

यदि आपको केवल आउटपुट का उपयोग करने के लिए बैकटिक्स की आवश्यकता है।

मुझे STDOUT और STDERR जैसे अधिक उन्नत सामान की आवश्यकता थी इसलिए मैंने Open4 रत्न का उपयोग किया। आपके पास वहां बताई गई सभी विधियां हैं।


2
यहाँ वर्णित पोस्ट %xवाक्यविन्यास विकल्प पर चर्चा नहीं करता है।
मेई

ओपन 4 के लिए +1। spawnजब मैंने यह पाया तो मैंने पहले से ही इसके तरीके के अपने संस्करण को लागू करने की कोशिश शुरू कर दी थी ।
ब्रेंडन

40

मेरा पसंदीदा Open3 है

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

2
मैं भी open3, विशेष रूप से Open3.capture3 की तरह: ruby-doc.org/stdlib-1.9.3/libdoc/open3/rdoc/... -> stdout, stderr, status = Open3.capture3('nroff -man', :stdin_data => stdin)
SEVERIN

क्या ओपेन 3 के साथ स्पेक और यूनिट टेस्टिंग करने का कोई डॉक्यूमेंटेशन है, या रूबी स्टैड-लाइब में अन्य ओपन है? मेरी समझ के मौजूदा स्तर पर शेल का परीक्षण करना कठिन है।
FilBot3

29

इन तंत्रों के बीच चयन करते समय सोचने वाली कुछ बातें हैं:

  1. क्या आप सिर्फ stdout चाहते हैं या आपको stderr की भी आवश्यकता है? या अलग भी हो गए?
  2. आपका आउटपुट कितना बड़ा है? क्या आप पूरे परिणाम को स्मृति में रखना चाहते हैं?
  3. क्या आप अपने कुछ आउटपुट पढ़ना चाहते हैं जबकि सबप्रोसेस अभी भी चल रहा है?
  4. क्या आपको परिणाम कोड की आवश्यकता है?
  5. क्या आपको एक रूबी ऑब्जेक्ट की आवश्यकता है जो प्रक्रिया का प्रतिनिधित्व करती है और आपको इसे मांग पर मारने देती है?

आपको साधारण बैकटिक्स (``) system(), और IO.popenपूर्ण-विकसित Kernel.fork/ के Kernel.execसाथ IO.pipeऔर से कुछ भी की आवश्यकता हो सकती है IO.select

यदि आप एक सब-प्रोसेस को निष्पादित करने में बहुत लंबा समय लेते हैं, तो आप मिश्रण में टाइमआउट फेंकना चाहते हैं।

दुर्भाग्य से, यह बहुत निर्भर करता है


25

एक और विकल्प:

जब आप:

  • stderr के साथ ही stdout की जरूरत है
  • Open3 / Open4 का उपयोग नहीं कर सकते हैं (वे मेरे मैक पर NetBeans में अपवाद फेंकते हैं, पता नहीं क्यों)

आप शेल पुनर्निर्देशन का उपयोग कर सकते हैं:

puts %x[cat bogus.txt].inspect
  => ""

puts %x[cat bogus.txt 2>&1].inspect
  => "cat: bogus.txt: No such file or directory\n"

2>&1वाक्य रचना में काम करता है लिनक्स , मैक और विंडोज MS-DOS के शुरुआती दिनों से।


25

मैं निश्चित रूप से रूबी विशेषज्ञ नहीं हूं, लेकिन मैं इसे एक शॉट दूंगा:

$ irb 
system "echo Hi"
Hi
=> true

आपको इस तरह की चीजें करने में सक्षम होना चाहिए:

cmd = 'ls'
system(cmd)

21

उपरोक्त उत्तर पहले से ही काफी शानदार हैं, लेकिन मैं वास्तव में निम्नलिखित सारांश लेख साझा करना चाहता हूं: " रूबी में शैल कमांड चलाने के 6 तरीके "

असल में, यह हमें बताता है:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

systemऔर $?:

system 'false' 
puts $?

बैकटिक्स (`):

today = `date`

IO#popen:

IO.popen("date") { |f| puts f.gets }

Open3#popen3 - stdlib:

require "open3"
stdin, stdout, stderr = Open3.popen3('dc') 

Open4#popen4 -- एक रत्न:

require "open4" 
pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]

15

यदि आपको वास्तव में "सर्वश्रेष्ठ" उत्तर में नोट के अनुसार बैश की आवश्यकता है।

सबसे पहले, ध्यान दें कि जब रूबी एक शेल को कॉल करती है, तो यह आम तौर पर कॉल करता है /bin/sh, न कि बैश। कुछ बैश सिंटैक्स /bin/shसभी सिस्टम पर समर्थित नहीं हैं।

यदि आपको बैश का उपयोग करने की आवश्यकता है, bash -c "your Bash-only command"तो अपनी इच्छित कॉलिंग विधि के अंदर डालें :

quick_output = system("ls -la")
quick_bash = system("bash -c 'ls -la'")

परीक्षा करना:

system("echo $SHELL")
system('bash -c "echo $SHELL"')

या यदि आप एक मौजूदा स्क्रिप्ट फ़ाइल की तरह चल रहे हैं

script_output = system("./my_script.sh")

रूबी को शेबंग का सम्मान करना चाहिए , लेकिन आप हमेशा उपयोग कर सकते हैं

system("bash ./my_script.sh")

यह सुनिश्चित करने के लिए, हालाँकि /bin/shदौड़ने से थोड़ी अधिक परेशानी हो सकती है /bin/bash, आप शायद ध्यान नहीं देंगे।


11

आप पर्ल के समान बैकटिक ऑपरेटर (`) का भी उपयोग कर सकते हैं:

directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory

आसान अगर आपको कुछ आसान चाहिए।

आप किस विधि का उपयोग करना चाहते हैं यह ठीक उसी पर निर्भर करता है जिसे आप पूरा करने की कोशिश कर रहे हैं; विभिन्न तरीकों के बारे में अधिक जानकारी के लिए डॉक्स जांचें।


10

हम इसे कई तरीकों से हासिल कर सकते हैं।

का उपयोग करते हुए Kernel#exec, कुछ भी नहीं के बाद इस आदेश को क्रियान्वित किया जाता है:

exec('ls ~')

का उपयोग करते हुए backticks or %x

`ls ~`
=> "Applications\nDesktop\nDocuments"
%x(ls ~)
=> "Applications\nDesktop\nDocuments"

Kernel#systemकमांड का उपयोग करना , trueयदि सफल हो, falseतो असफल हो और nilयदि फाँसी की सजा विफल हो तो वापस लौटना:

system('ls ~')
=> true


9

यहाँ के जवाबों का उपयोग करना और मिहाई के उत्तर में जुड़ा हुआ है, मैंने एक ऐसा कार्य किया है जो इन आवश्यकताओं को पूरा करता है:

  1. नीटली STDOUT और STDERR को पकड़ लेती है इसलिए जब मेरी स्क्रिप्ट कंसोल से चलती है तो वे "लीक" नहीं होती हैं।
  2. तर्क को एक सरणी के रूप में पारित करने की अनुमति देता है, इसलिए भागने की चिंता करने की कोई आवश्यकता नहीं है।
  3. कमांड की निकास स्थिति को कैप्चर करता है इसलिए जब कोई त्रुटि हुई है तो यह स्पष्ट है।

एक बोनस के रूप में, यह उन मामलों में भी STDOUT लौटेगा जहां शेल कमांड सफलतापूर्वक (0) से बाहर निकलता है और STDOUT पर कुछ भी डालता है। इस तरीके से, यह अलग है system, जो केवल trueऐसे मामलों में लौटता है।

कोड इस प्रकार है। विशिष्ट कार्य है system_quietly:

require 'open3'

class ShellError < StandardError; end

#actual function:
def system_quietly(*cmd)
  exit_status=nil
  err=nil
  out=nil
  Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
    err = stderr.gets(nil)
    out = stdout.gets(nil)
    [stdin, stdout, stderr].each{|stream| stream.send('close')}
    exit_status = wait_thread.value
  end
  if exit_status.to_i > 0
    err = err.chomp if err
    raise ShellError, err
  elsif out
    return out.chomp
  else
    return true
  end
end

#calling it:
begin
  puts system_quietly('which', 'ruby')
rescue ShellError
  abort "Looks like you don't have the `ruby` command. Odd."
end

#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"

9

spawnनिर्दिष्ट कमांड को निष्पादित करने के लिए पृष्ठभूमि प्रक्रिया बनाने के लिए कमांड को मत भूलना । तुम भी Processवर्ग और लौटे का उपयोग कर इसके पूरा होने की प्रतीक्षा कर सकते हैं pid:

pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid

pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid

डॉक्टर कहते हैं: यह तरीका समान है #systemलेकिन यह कमांड के खत्म होने का इंतजार नहीं करता है।


2
Kernel.spawn()अन्य सभी विकल्पों की तुलना में कहीं अधिक बहुमुखी प्रतीत होता है।
कश्यप

6

यदि आपके पास उस सामान्य मामले की तुलना में अधिक जटिल मामला है जिसे संभाला नहीं जा सकता है ``, तो देखें Kernel.spawn()। यह बाहरी आदेशों को निष्पादित करने के लिए स्टॉक रूबी द्वारा प्रदान की गई सबसे सामान्य / पूर्ण विशेषताओं वाली प्रतीत होती है।

आप इसका उपयोग कर सकते हैं:

  • प्रक्रिया समूह (विंडोज़) बनाएँ।
  • फ़ाइलों / प्रत्येक-दूसरे के लिए, बाहर, त्रुटि को पुनर्निर्देशित करें।
  • env var, umask सेट करें।
  • कमांड निष्पादित करने से पहले डायरेक्टरी को बदलें।
  • CPU / डेटा / आदि के लिए संसाधन सीमाएँ निर्धारित करें।
  • वह सब करें जो अन्य विकल्पों में अन्य विकल्पों के साथ किया जा सकता है, लेकिन अधिक कोड के साथ।

रूबी प्रलेखन काफी अच्छा उदाहरण है:

env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
command...:
  commandline                 : command line string which is passed to the standard shell
  cmdname, arg1, ...          : command name and one or more arguments (no shell)
  [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
  clearing environment variables:
    :unsetenv_others => true   : clear environment variables except specified by env
    :unsetenv_others => false  : dont clear (default)
  process group:
    :pgroup => true or 0 : make a new process group
    :pgroup => pgid      : join to specified process group
    :pgroup => nil       : dont change the process group (default)
  create new process group: Windows only
    :new_pgroup => true  : the new process is the root process of a new process group
    :new_pgroup => false : dont create a new process group (default)
  resource limit: resourcename is core, cpu, data, etc.  See Process.setrlimit.
    :rlimit_resourcename => limit
    :rlimit_resourcename => [cur_limit, max_limit]
  current directory:
    :chdir => str
  umask:
    :umask => int
  redirection:
    key:
      FD              : single file descriptor in child process
      [FD, FD, ...]   : multiple file descriptor in child process
    value:
      FD                        : redirect to the file descriptor in parent process
      string                    : redirect to file with open(string, "r" or "w")
      [string]                  : redirect to file with open(string, File::RDONLY)
      [string, open_mode]       : redirect to file with open(string, open_mode, 0644)
      [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
      [:child, FD]              : redirect to the redirected file descriptor
      :close                    : close the file descriptor in child process
    FD is one of follows
      :in     : the file descriptor 0 which is the standard input
      :out    : the file descriptor 1 which is the standard output
      :err    : the file descriptor 2 which is the standard error
      integer : the file descriptor of specified the integer
      io      : the file descriptor specified as io.fileno
  file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
    :close_others => false : inherit fds (default for system and exec)
    :close_others => true  : dont inherit (default for spawn and IO.popen)

6

रूबी से शेल कमांड को कॉल करने के लिए बैकटिक्स (`) विधि सबसे आसान है। यह शेल कमांड का परिणाम देता है:

     url_request = 'http://google.com'
     result_of_shell_command = `curl #{url_request}`

5

जैसी आज्ञा दी जाती है attrib:

require 'open3'

a="attrib"
Open3.popen3(a) do |stdin, stdout, stderr|
  puts stdout.read
end

मैंने पाया है कि जबकि यह तरीका उतना यादगार नहीं है

system("thecommand")

या

`thecommand`

backticks में, अन्य तरीकों की तुलना में इस विधि के बारे में एक अच्छी बात यह है कि backticks मुझे putsउस कमांड को चलाने की अनुमति नहीं देता है जिसे मैं एक वेरिएबल में चलाना चाहता हूं और जो कमांड मैं स्टोर करना चाहता / चाहती हूं, system("thecommand")वह मुझे आउटपुट नहीं मिलने देता है जबकि यह विधि मुझे उन दोनों चीजों को करने देती है, और यह मुझे स्वतंत्र रूप से स्टड, स्टडआउट और स्टेडर तक पहुंचने देती है।

" माणिक में कार्यकारी आदेश " और रूबी के ओपन 3 प्रलेखन देखें


3

यह वास्तव में एक उत्तर नहीं है, लेकिन शायद कोई इसे उपयोगी पाएगा:

विंडोज पर TK GUI का उपयोग करते समय, और आपको रूबी से शेल कमांड को कॉल करने की आवश्यकता होती है, आपको हमेशा एक कष्टप्रद सीएमडी विंडो कम से कम एक सेकंड के लिए पॉपिंग होगी।

इससे बचने के लिए आप इसका उपयोग कर सकते हैं:

WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)

या

WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)

दोनों ipconfigआउटपुट को अंदर स्टोर करेंगे log.txt, लेकिन कोई विंडो नहीं आएगी।

आपको require 'win32ole'अपनी स्क्रिप्ट के अंदर जाने की आवश्यकता होगी ।

system(), exec()और spawn()टीके और रूबी का उपयोग करते समय सभी उस कष्टप्रद विंडो को पॉप अप करेंगे।


-2

यहाँ एक अच्छा है जिसे मैं OS X पर एक रूबी स्क्रिप्ट में उपयोग करता हूं (ताकि मैं एक स्क्रिप्ट शुरू कर सकूं और खिड़की से दूर जाने के बाद भी अपडेट प्राप्त कर सकूं):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.