如果我调用一个命令使用内核#系统在Ruby中,我如何得到它的输出?
system("ls")
如果我调用一个命令使用内核#系统在Ruby中,我如何得到它的输出?
system("ls")
当前回答
你可以用反勾号:
`ls`
其他回答
我发现下面是有用的,如果你需要返回值:
result = %x[ls]
puts result
我特别想列出我的机器上所有Java进程的pid,并使用以下方法:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
如果你需要转义参数,在Ruby 1.9 IO中。Popen也接受数组:
p IO.popen(["echo", "it's escaped"]).read
在早期版本中,你可以使用Open3.popen3:
require "open3"
Open3.popen3("echo", "it's escaped") { |i, o| p o.read }
如果你还需要传递stdin,这应该在1.9和1.8中都有效:
out = IO.popen("xxd -p", "r+") { |io|
io.print "xyz"
io.close_write
io.read.chomp
}
p out # "78797a"
正确而安全地做到这一点的简单方法是使用Open3.capture2()、Open3.capture2e()或Open3.capture3()。
如果与不受信任的数据一起使用ruby的反刻度号和它的%x别名在任何情况下都是不安全的。这很危险,简单明了:
untrusted = "; date; echo"
out = `echo #{untrusted}` # BAD
untrusted = '"; date; echo"'
out = `echo "#{untrusted}"` # BAD
untrusted = "'; date; echo'"
out = `echo '#{untrusted}'` # BAD
相反,如果使用正确,系统函数会正确转义参数:
ret = system "echo #{untrusted}" # BAD
ret = system 'echo', untrusted # good
问题是,它返回的是退出代码而不是输出,捕获后者是复杂而混乱的。
到目前为止,这篇文章中最好的答案提到了Open3,但没有提到最适合这项任务的函数。Open3。Capture2, capture2e和capture3的工作方式类似system,但返回两个或三个参数:
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
另一个提到了IO.popen()。从它想要一个数组作为输入的意义上来说,语法可能是笨拙的,但它也可以工作:
out = IO.popen(['echo', untrusted]).read # good
为了方便起见,你可以将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
例子:
p system('foo')
p syscall('foo')
p system('which', 'foo')
p syscall('which', 'foo')
p system('which', 'which')
p syscall('which', 'which')
产生如下结果:
nil
nil
false
false
/usr/bin/which <— stdout from system('which', 'which')
true <- p system('which', 'which')
"/usr/bin/which" <- p syscall('which', 'which')
请注意,所有向系统传递包含用户提供值的字符串的解决方案,%x[]等都是不安全的!不安全实际上意味着:用户可以触发代码在上下文中运行,并具有程序的所有权限。
据我所知,只有系统和Open3。popen3在Ruby 1.8中提供了一个安全/转义的变体。在Ruby 1.9中,IO::popen也接受数组。
只需将每个选项和参数作为数组传递给其中一个调用。
如果你不仅需要退出状态,还需要结果,你可能会使用Open3.popen3:
require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3('usermod', '-p', @options['shadow'], @options['username'])
stdout.gets(nil)
stdout.close
stderr.gets(nil)
stderr.close
exit_code = wait_thr.value
注意,块表单将自动关闭stdin, stdout和stderr-否则它们必须显式关闭。
更多信息:在Ruby中形成卫生shell命令或系统调用
虽然使用反勾号或popen通常是您真正想要的,但它实际上并不能回答所提出的问题。可能有捕获系统输出的正当理由(可能是为了自动化测试)。我在谷歌上搜索了一下,找到了一个答案,我想把它贴在这里,以造福他人。
因为我需要这个来测试我的例子使用块设置来捕获标准输出,因为实际的系统调用隐藏在被测试的代码中:
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
该方法使用tempfile存储实际数据来捕获给定块中的任何输出。使用示例:
captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content
您可以将系统调用替换为任何内部调用系统的东西。如果需要,还可以使用类似的方法来捕获stderr。