從Ruby調用shell命令




interop (13)

上面的答案已經相當不錯了,但我真的很想分享下面的總結文章:“ http://tech.natemurray.com/2007/03/ruby-shell-commands.html

基本上,它告訴我們:

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

如何從Ruby程序內部調用shell命令? 那我如何從這些命令的輸出回到Ruby?


不要忘記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類似,但不等待命令完成。


在我看來,關於在Ruby中運行shell腳本的最佳文章是:“在Ruby http://tech.natemurray.com/2007/03/ruby-shell-commands.html ”。

如果你只需要得到輸出使用反引號。

我需要更高級的東西,比如STDOUT和STDERR,所以我使用了Open4 gem。 你在那裡解釋了所有的方法。


在這些機制之間進行選擇時需要考慮的一些事情是:

  1. 你只是想要stdout還是你需要stderr? 甚至分離出來?
  2. 你的輸出有多大? 你想把整個結果保存在內存中嗎?
  3. 當子進程仍在運行時,你想讀取一些輸出嗎?
  4. 你需要結果代碼嗎?
  5. 你需要一個代表這個過程的紅寶石物體,並且可以讓它按需要殺死它嗎?

您可能需要從簡單的反引號(``),system()和IO.popenIO.pipeIO.select的全面Kernel.fork / IO.select

如果一個子進程執行時間過長,您可能還想要將混合超時添加到組合中。

不幸的是,它非常依賴


如果你真的需要Bash,請按照“最佳”答案中的註釋。

首先,請注意,當Ruby調出一個shell時,它通常會調用/bin/sh 而不是 Bash。 在所有系統上,某些Bash語法不受/bin/sh支持。

如果您需要使用Bash, bash -c "your Bash-only command"在所需的調用方法內插入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") ),Ruby 應該遵守shebang,但您可以始終使用system("bash ./my_script.sh")來確保儘管從/bin/sh運行/bin/bash可能會有些許開銷,但您可能不會注意到。


您也可以使用類似於Perl的反引號操作符(`):

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

方便,如果你需要一些簡單的東西。

你想使用哪種方法取決於你想要完成的工作; 請查看文檔以獲取有關不同方法的更多詳細信息。


我喜歡這樣做的方式是使用%x文字,這使得在命令中使用引號變得容易(並且易讀!),如下所示:

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

在這種情況下,它將在當前目錄下使用所有測試文件填充文件列表,您可以按預期進行處理:

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

我最喜歡的是Open3

  require "open3"

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

最簡單的方法是,例如:

reboot = `init 6`
puts reboot

給定一個命令,例如attrib

require 'open3'

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

我發現雖然這種方法不像系統(“命令”)或反引號中的命令那樣令人難忘,但與其他方法相比,這種方法的好處在於例如反引號似乎不讓我'提出'命令我運行/存儲我想要在一個變量中運行的命令,而system(“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


這是一個基於這個答案的流程圖。 另請參閱使用script模擬終端 。


這是一個很酷的,我在OS X的ruby腳本中使用(這樣我就可以啟動腳本並在從窗口切換之後獲得更新):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )

  • 反引用`方法是從ruby調用shell命令最簡單的方法。 它返回shell命令的結果。

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




interop