本文翻译自:Getting output of system() calls in Ruby
如果我在Ruby中使用Kernel#system调用命令,我该如何获得它的输出?
system("ls")
#1楼
参考:https://stackoom.com/question/2tXT/在Ruby中获取system-调用的输出
#2楼
If you need to escape the arguments, in Ruby 1.9 IO.popen also accepts an array: 如果你需要转义参数,在Ruby 1.9 IO.popen也接受一个数组:
p IO.popen(["echo", "it's escaped"]).read
In earlier versions you can use Open3.popen3 : 在早期版本中,您可以使用Open3.popen3 :
require "open3"
Open3.popen3("echo", "it's escaped") { |i, o| p o.read }
If you also need to pass stdin, this should work in both 1.9 and 1.8: 如果你还需要传递stdin,这应该适用于1.9和1.8:
out = IO.popen("xxd -p", "r+") { |io|
io.print "xyz"
io.close_write
io.read.chomp
}
p out # "78797a"
#3楼
I'd like to expand & clarify chaos's answer a bit. 我想扩大和澄清一下混乱的答案 。
If you surround your command with backticks, then you don't need to (explicitly) call system() at all. 如果用反引号包围命令,则根本不需要(显式地)调用system()。 The backticks execute the command and return the output as a string. 反引号执行命令并将输出作为字符串返回。 You can then assign the value to a variable like so: 然后,您可以将值分配给变量,如下所示:
output = `ls`
p output
or 要么
printf output # escapes newline chars
#4楼
While using backticks or popen is often what you really want, it doesn't actually answer the question asked. 虽然使用反引号或popen通常是你真正想要的,它实际上并没有回答问题。 There may be valid reasons for capturing system
output (maybe for automated testing). 捕获system
输出可能有正当理由(可能用于自动化测试)。 A little Googling turned up an answer I thought I would post here for the benefit of others. 一个小小的谷歌搜索出现了一个答案,我想我会在这里发布,以造福他人。
Since I needed this for testing my example uses a block setup to capture the standard output since the actual system
call is buried in the code being tested: 由于我需要这个用于测试我的示例使用块设置来捕获标准输出,因为实际的system
调用隐藏在被测试的代码中:
require 'tempfile'
def capture_stdout
stdout = $stdout.dup
Tempfile.open 'stdout-redirect' do |temp|
$stdout.reopen temp.path, 'w+'
yield if block_given?
$stdout.reopen stdout
temp.read
end
end
This method captures any output in the given block using a tempfile to store the actual data. 此方法使用临时文件捕获给定块中的任何输出以存储实际数据。 Example usage: 用法示例:
captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content
You can replace the system
call with anything that internally calls system
. 您可以使用内部调用system
任何内容替换system
调用。 You could also use a similar method to capture stderr
if you wanted. 如果需要,您也可以使用类似的方法捕获stderr
。
#5楼
If you want the output redirected to a file using Kernel#system
, you can do modify descriptors like this: 如果您希望使用Kernel#system
将输出重定向到文件,则可以执行以下修改描述符:
redirect stdout and stderr to a file(/tmp/log) in append mode: 在追加模式下将stdout和stderr重定向到文件(/ tmp / log):
system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])
For a long running command, this will store the output in real time. 对于长时间运行的命令,这将实时存储输出。 You can also, store the output using a IO.pipe and redirect it from Kernel#system. 您还可以使用IO.pipe存储输出,并从Kernel#system重定向它。
#6楼
The straightforward way to do this correctly and securely is to use Open3.capture2()
, Open3.capture2e()
, or Open3.capture3()
. 正确而安全地执行此操作的直接方法是使用Open3.capture2()
, Open3.capture2e()
或Open3.capture3()
。
Using ruby's backticks and its %x
alias are NOT SECURE UNDER ANY CIRCUMSTANCES if used with untrusted data. 如果与不受信任的数据一起使用,使用ruby的反引号及其%x
别名在任何情况下都不安全 。 It is DANGEROUS , plain and simple: 危险 ,简单明了:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
The system
function, in contrast, escapes arguments properly if used correctly : 相反, 如果正确使用 , system
函数会正确地转义参数:
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
Trouble is, it returns the exit code instead of the output, and capturing the latter is convoluted and messy. 麻烦的是,它返回退出代码而不是输出,捕获后者是错综复杂的。
The best answer in this thread so far mentions Open3, but not the functions that are best suited for the task. 到目前为止,该线程中的最佳答案提到了Open3,但没有提到最适合该任务的函数。 Open3.capture2
, capture2e
and capture3
work like system
, but returns two or three arguments: Open3.capture2
, capture2e
和capture3
像system
capture3
工作,但返回两个或三个参数:
out, err, st = Open3.capture3("echo #{untrusted}") # BAD
out, err, st = Open3.capture3('echo', untrusted) # good
out_err, st = Open3.capture2e('echo', untrusted) # good
out, st = Open3.capture2('echo', untrusted) # good
p st.exitstatus
Another mentions IO.popen()
. 另一个提到了IO.popen()
。 The syntax can be clumsy in the sense that it wants an array as input, but it works too: 语法可能是笨拙的,因为它想要一个数组作为输入,但它也有效:
out = IO.popen(['echo', untrusted]).read # good
For convenience, you can wrap Open3.capture3()
in a function, eg: 为方便起见,您可以将Open3.capture3()
包装在一个函数中,例如:
#
# Returns stdout on success, false on failure, nil on error
#
def syscall(*cmd)
begin
stdout, stderr, status = Open3.capture3(*cmd)
status.success? && stdout.slice!(0..-(1 + $/.size)) # strip trailing eol
rescue
end
end
Example: 例:
p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')
Yields the following: 产量如下:
nil
nil
false
false
/usr/bin/which <— stdout from system('which', 'which')
true <- p system('which', 'which')
"/usr/bin/which" <- p syscall('which', 'which')