استدعاء أوامر shell من روبي
سؤال
كيف يمكنني استدعاء أوامر shell من داخل برنامج روبي؟كيف يمكنني بعد ذلك الحصول على الإخراج من هذه الأوامر مرة أخرى إلى روبي؟
المحلول
ويستند هذا التفسير على تعليق روبي النصي من صديق لي.إذا كنت ترغب في تحسين البرنامج النصي، فلا تتردد في تحديثه على الرابط.
أولاً، لاحظ أنه عندما تستدعي روبي الصدفة، فإنها تتصل عادةً /bin/sh
, لا سحق.بعض صيغ Bash غير مدعومة /bin/sh
على جميع الأنظمة.
فيما يلي طرق لتنفيذ برنامج نصي Shell:
cmd = "echo 'hi'" # Sample string that can be used
Kernel#`
، تسمى عادة backticks -`cmd`
وهذا مثل العديد من اللغات الأخرى، بما في ذلك Bash وPHP وPerl.
إرجاع نتيجة أمر الصدفة.
المستندات: http://Ruby-doc.org/core/Kernel.html#method-i-60
value = `echo 'hi'` value = `#{cmd}`
بناء الجملة المدمج،
%x( cmd )
بعد
x
الحرف هو المحدد، والذي يمكن أن يكون أي حرف.إذا كان المحدد هو أحد الأحرف(
,[
,{
, ، أو<
، يتكون الحرفي من الشخصيات حتى محدد إغلاق المطابقة ، مع مراعاة أزواج محدد متداخلة.بالنسبة لجميع المحددات الأخرى ، تضم الحرفي الشخصيات حتى الحدوث التالي لشخصية تحديد المسلح.استيفاء السلسلة#{ ... }
مسموح به.يُرجع نتيجة أمر الصدفة، تمامًا مثل علامات التحديد الخلفية.
المستندات: http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html
value = %x( echo 'hi' ) value = %x[ #{cmd} ]
Kernel#system
ينفذ الأمر المحدد في قذيفة فرعية.
عائدات
true
إذا تم العثور على الأمر وتشغيله بنجاح،false
خلاف ذلك.المستندات: http://Ruby-doc.org/core/Kernel.html#method-i-system
wasGood = system( "echo 'hi'" ) wasGood = system( cmd )
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
لمحاكاة محطة.
الطريقة التي أحب القيام بها هي استخدام %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| ... }
بعض الأشياء التي يجب التفكير فيها عند الاختيار بين هذه الآليات هي:
- هل تريد فقط stdout أو هل تحتاج إلى stderr كذلك؟أو حتى انفصلت؟
- ما هو حجم انتاجك؟هل تريد الاحتفاظ بالنتيجة الكاملة في الذاكرة؟
- هل تريد قراءة بعض الإخراج الخاص بك بينما لا تزال العملية الفرعية قيد التشغيل؟
- هل تحتاج إلى رموز النتائج؟
- هل تحتاج إلى كائن روبي يمثل العملية ويتيح لك قتلها عند الطلب؟
قد تحتاج إلى أي شيء بدءًا من العلامات الخلفية البسيطة (``) والنظام () و 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
باستخدام الإجابات هنا والمرتبطة بإجابة ميهاي، قمت بتجميع وظيفة تلبي هذه المتطلبات:
- يلتقط STDOUT وSTDERR بدقة حتى لا "يتسربا" عند تشغيل البرنامج النصي الخاص بي من وحدة التحكم.
- يسمح بتمرير الوسائط إلى الصدفة كمصفوفة، لذلك لا داعي للقلق بشأن الهروب.
- يلتقط حالة الخروج للأمر بحيث يكون واضحًا عند حدوث خطأ.
كمكافأة، سيعيد هذا أيضًا 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 )