سؤال

كيف يمكنني استدعاء أوامر shell من داخل برنامج روبي؟كيف يمكنني بعد ذلك الحصول على الإخراج من هذه الأوامر مرة أخرى إلى روبي؟

هل كانت مفيدة؟

المحلول

ويستند هذا التفسير على تعليق روبي النصي من صديق لي.إذا كنت ترغب في تحسين البرنامج النصي، فلا تتردد في تحديثه على الرابط.

أولاً، لاحظ أنه عندما تستدعي روبي الصدفة، فإنها تتصل عادةً /bin/sh, لا سحق.بعض صيغ Bash غير مدعومة /bin/sh على جميع الأنظمة.

فيما يلي طرق لتنفيذ برنامج نصي Shell:

cmd = "echo 'hi'" # Sample string that can be used
  1. Kernel#` ، تسمى عادة backticks - `cmd`

    وهذا مثل العديد من اللغات الأخرى، بما في ذلك Bash وPHP وPerl.

    إرجاع نتيجة أمر الصدفة.

    المستندات: http://Ruby-doc.org/core/Kernel.html#method-i-60

    value = `echo 'hi'`
    value = `#{cmd}`
    
  2. بناء الجملة المدمج، %x( cmd )

    بعد x الحرف هو المحدد، والذي يمكن أن يكون أي حرف.إذا كان المحدد هو أحد الأحرف (, [, {, ، أو <، يتكون الحرفي من الشخصيات حتى محدد إغلاق المطابقة ، مع مراعاة أزواج محدد متداخلة.بالنسبة لجميع المحددات الأخرى ، تضم الحرفي الشخصيات حتى الحدوث التالي لشخصية تحديد المسلح.استيفاء السلسلة #{ ... } مسموح به.

    يُرجع نتيجة أمر الصدفة، تمامًا مثل علامات التحديد الخلفية.

    المستندات: http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html

    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

لمزيد من القراءة راجع:

نصائح أخرى

فيما يلي مخطط انسيابي يعتمد على هذه الإجابة.أنظر أيضا، استخدام script لمحاكاة محطة.

enter image description here

الطريقة التي أحب القيام بها هي استخدام %x حرفي، مما يجعل من السهل (وقابل للقراءة!) استخدام علامات الاقتباس في الأمر، كما يلي:

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

والذي، في هذه الحالة، سوف يقوم بملء قائمة الملفات بجميع ملفات الاختبار الموجودة ضمن الدليل الحالي، والتي يمكنك معالجتها كما هو متوقع:

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

إليك أفضل مقال في رأيي حول تشغيل البرامج النصية لـ Shell في روبي:"6 طرق لتشغيل أوامر Shell في روبي".

إذا كنت تحتاج فقط إلى الحصول على الناتج، استخدم backticks.

كنت بحاجة إلى المزيد من الأشياء المتقدمة مثل STDOUT وSTDERR لذا استخدمت جوهرة Open4.لقد شرحت كل الطرق هناك.

المفضل لدي هو فتح3

  require "open3"

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

بعض الأشياء التي يجب التفكير فيها عند الاختيار بين هذه الآليات هي:

  1. هل تريد فقط stdout أو هل تحتاج إلى stderr كذلك؟أو حتى انفصلت؟
  2. ما هو حجم انتاجك؟هل تريد الاحتفاظ بالنتيجة الكاملة في الذاكرة؟
  3. هل تريد قراءة بعض الإخراج الخاص بك بينما لا تزال العملية الفرعية قيد التشغيل؟
  4. هل تحتاج إلى رموز النتائج؟
  5. هل تحتاج إلى كائن روبي يمثل العملية ويتيح لك قتلها عند الطلب؟

قد تحتاج إلى أي شيء بدءًا من العلامات الخلفية البسيطة (``) والنظام () و IO.popen إلى كامل Kernel.fork/Kernel.exec مع IO.pipe و IO.select.

قد ترغب أيضًا في إلغاء المهلات في المزيج إذا استغرق تنفيذ العملية الفرعية وقتًا طويلاً.

لسوء الحظ، كثيرا جدا يعتمد على.

خيار آخر:

عندما انت:

  • تحتاج stderr وكذلك stdout
  • لا يمكنهم/لن يستخدموا Open3/Open4 (إنهم يطرحون استثناءات في NetBeans على جهاز Mac الخاص بي، ولا يعرفون السبب)

يمكنك استخدام إعادة توجيه Shell:

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.

أنا بالتأكيد لست خبيرًا في روبي، لكنني سأجرب ذلك:

$ irb 
system "echo Hi"
Hi
=> true

يجب أن تكون أيضًا قادرًا على القيام بأشياء مثل:

cmd = 'ls'
system(cmd)

الإجابات المذكورة أعلاه رائعة جدًا بالفعل، ولكنني أرغب حقًا في مشاركة المقالة الموجزة التالية:"6 طرق لتشغيل أوامر Shell في روبي"

في الأساس، يخبرنا:

Kernel#exec:

exec 'echo "hello $HOSTNAME"'

system و $?:

system 'false' 
puts $?

العلامات الخلفية (`):

today = `date`

IO#popen:

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

Open3#popen3 -- ستدليب:

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>]

إذا كنت حقًا بحاجة إلى Bash، فوفقًا للملاحظة الواردة في الإجابة "الأفضل".

أولاً، لاحظ أنه عندما تستدعي روبي الصدفة، فإنها تتصل عادةً /bin/sh, لا سحق.بعض صيغ Bash غير مدعومة /bin/sh على جميع الأنظمة.

إذا كنت بحاجة إلى استخدام Bash، أدخل 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, ، ربما لن تلاحظ.

يمكنك أيضًا استخدام عوامل التشغيل (`)، المشابهة لـ Perl:

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

مفيد إذا كنت بحاجة إلى شيء بسيط.

تعتمد الطريقة التي تريد استخدامها على ما تحاول تحقيقه بالضبط؛تحقق من المستندات للحصول على مزيد من التفاصيل حول الطرق المختلفة.

أسهل طريقة هي على سبيل المثال:

reboot = `init 6`
puts reboot

باستخدام الإجابات هنا والمرتبطة بإجابة ميهاي، قمت بتجميع وظيفة تلبي هذه المتطلبات:

  1. يلتقط STDOUT وSTDERR بدقة حتى لا "يتسربا" عند تشغيل البرنامج النصي الخاص بي من وحدة التحكم.
  2. يسمح بتمرير الوسائط إلى الصدفة كمصفوفة، لذلك لا داعي للقلق بشأن الهروب.
  3. يلتقط حالة الخروج للأمر بحيث يكون واضحًا عند حدوث خطأ.

كمكافأة، سيعيد هذا أيضًا STDOUT في الحالات التي يخرج فيها أمر shell بنجاح (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"

يمكننا تحقيق ذلك بطرق متعددة.

استخدام Kernel#exec, لا شيء بعد تنفيذ هذا الأمر:

exec('ls ~')

استخدام backticks or %x

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

استخدام Kernel#system الأمر، يعود true إذا نجحت، false إذا لم ينجح ويعود nil إذا فشل تنفيذ الأمر:

system('ls ~')
=> true

لا تنسى 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 لكنه لا ينتظر انتهاء الأمر.

إذا كانت لديك حالة أكثر تعقيدًا من الحالة الشائعة (التي لا يمكن التعامل معها ``) ثم تحقق Kernel.spawn() هنا.يبدو أن هذا هو الأكثر عمومية/كاملة المواصفات التي تقدمها الأسهم روبي لتنفيذ الأوامر الخارجية.

على سبيل المثاليمكنك استخدامه من أجل:

  • إنشاء مجموعات العمليات (ويندوز)
  • إعادة التوجيه للداخل والخارج للخطأ في الملفات/بعضها البعض.
  • تعيين env vars، umask
  • تغيير دير قبل تنفيذ الأمر
  • تعيين حدود الموارد لوحدة المعالجة المركزية/البيانات/...
  • افعل كل ما يمكن فعله باستخدام الخيارات الأخرى في الإجابات الأخرى، ولكن باستخدام المزيد من التعليمات البرمجية.

رسمي وثائق روبي لديه أمثلة جيدة بما فيه الكفاية.

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)
  • طريقة backticks هي الطريقة الأسهل لاستدعاء أوامر Shell من روبي.تقوم بإرجاع نتيجة أمر shell.

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

نظرا لأمر على سبيل المثال attrib

require 'open3'

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

لقد وجدت أنه على الرغم من أن هذه الطريقة لا تُنسى مثل على سبيل المثال.system("thecommand") أو الأمر في العلامات الخلفية، شيء جيد في هذه الطريقة مقارنة بالطرق الأخرى..على سبيل المثاللا يبدو أن backticks تسمح لي بوضع الأمر الذي أقوم بتشغيله/تخزين الأمر الذي أريد تشغيله في متغير، ويبدو أن النظام ("thecommand") لا يسمح لي بالحصول على الإخراج.حيث أن هذه الطريقة تتيح لي القيام بكلا الأمرين، وتتيح لي الوصول إلى stdin وstdout وstderr بشكل مستقل.

https://blog.bigbinary.com/2012/10/18/backtick-system-exec-in-ruby.html

http://Ruby-doc.org/stdlib-2.4.1/libdoc/open3/rdoc/Open3.html

ليست إجابة حقًا ولكن ربما يجد شخص ما هذا مفيدًا، وهذا يتعلق بهذا الأمر.

عند استخدام TK GUI على نظام التشغيل Windows، وتحتاج إلى استدعاء أوامر Shell من Rubyw، سيكون لديك دائمًا نافذة cmd مزعجة تظهر لمدة أقل من ثانية.

لتجنب هذا يمكنك استخدام

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() ستظهر تلك النافذة المزعجة عند استخدام TK وRubyw.

إليك برنامجًا رائعًا أستخدمه في برنامج نصي روبي على نظام التشغيل OS X (حتى أتمكن من بدء برنامج نصي والحصول على تحديث حتى بعد التبديل بعيدًا عن النافذة):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top