在Ruby中获取system()调用的输出

如果我在Ruby中使用内核#系统调用一个命令,我如何得到它的输出?

system("ls")
288675 次浏览

你可以用反勾号:

`ls`

另一种方法是:

f = open("|ls")
foo = f.read()

注意,这是open中“ls”前面的“pipe”字符。这也可用于将数据送入程序的标准输入以及读取其标准输出。

我想扩大&澄清混乱的回答一点。

如果你用反勾号包围命令,那么你根本不需要(显式地)调用system()。反勾号执行命令并以字符串形式返回输出。然后你可以像这样把值赋给一个变量:

output = `ls`
p output

printf output # escapes newline chars

我发现下面是有用的,如果你需要返回值:

result = %x[ls]
puts result

我特别想列出我的机器上所有Java进程的pid,并使用以下方法:

ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]

作为直接系统(…)替代品,您可以使用Open3.popen3(…)

< p >进一步讨论: http://tech.natemurray.com/2007/03/ruby-shell-commands.html < / p >

只是为了记录,如果你想要(输出和操作结果),你可以这样做:

output=`ls no_existing_file` ;  result=$?.success?

您可以使用system()或%x[],这取决于您需要什么样的结果。

System()如果找到命令并成功运行,则返回true,否则返回false。

>> s = system 'uptime'
10:56  up 3 days, 23:10, 2 users, load averages: 0.17 0.17 0.14
=> true
>> s.class
=> TrueClass
>> $?.class
=> Process::Status

% x [. .]另一方面,将命令的结果保存为字符串:

>> result = %x[uptime]
=> "13:16  up 4 days,  1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> p result
"13:16  up 4 days,  1:30, 2 users, load averages: 0.39 0.29 0.23\n"
>> result.class
=> String

Th Jay Fields的博客文章详细解释了使用system, exec和%x[..]]。

请注意,所有将包含用户提供值的字符串传递给system%x[]等的解决方案都是不安全的!不安全实际上意味着:用户可以触发代码在上下文中运行,并具有程序的所有权限。

据我所知,在Ruby 1.8中只有systemOpen3.popen3提供了一个安全/转义的变体。在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命令或系统调用

如果你需要转义参数,在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"

虽然使用反勾号或popen通常是您真正想要的,但它实际上并不能回答所提出的问题。捕获system输出可能有正当的理由(可能是为了自动测试)。一点谷歌找到了答案,我想我将在这里发表,以造福他人。

因为我需要这个来测试,所以我的例子使用了块设置来捕获标准输出,因为实际的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

该方法使用tempfile存储实际数据来捕获给定块中的任何输出。使用示例:

captured_content = capture_stdout do
system 'echo foo'
end
puts captured_content

可以将system调用替换为任何内部调用system的调用。如果需要,也可以使用类似的方法来捕获stderr

如果你想使用Kernel#system将输出重定向到一个文件,你可以像这样修改描述符:

以追加模式将stdout和stderr重定向到文件(/tmp/log):

< p > <代码> System ('ls -al',:out =>['/tmp/log', 'a'],:err =>[' / tmp /日志',' ')) < /代码> < / p >

对于长时间运行的命令,这将实时存储输出。您还可以使用IO存储输出。管道和重定向它从内核#系统。

正确且安全的简单方法是使用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

相反,system函数正确地转义参数如果使用得当:

ret = system "echo #{untrusted}"                       # BAD
ret = system 'echo', untrusted                         # good

问题是,它返回的是退出代码而不是输出,捕获后者是复杂而混乱的。

到目前为止,这篇文章中最好的答案提到了Open3,但没有提到最适合这项任务的函数。Open3.capture2capture2ecapture3的工作方式类似于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')

作为西蒙Hürlimann已经解释过了Open3比反引号等更安全。

require 'open3'
output = Open3.popen3("ls") { |stdin, stdout, stderr, wait_thr| stdout.read }

注意,块表单将自动关闭stdin, stdout和stderr-否则它们必须为封闭的明确

puts `date`
puts $?




Mon Mar  7 19:01:15 PST 2016
pid 13093 exit 0

我在这里没有找到这个,所以添加它,我有一些问题得到完整的输出。

你可以重定向STDERR到STDOUT,如果你想捕获STDERR使用 撇号。< / p >

输出= ' grep hosts /private/etc/* 2>&1 '

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

将标准输出捕获到名为瓦尔的变量中最简单的解决方案:

val = capture(:stdout) do
system("pwd")
end


puts val

缩短版本:

val = capture(:stdout) { system("ls") }

捕获方法 active_support / core_ext /内核/ reporting.rb < / em >

类似地,我们也可以用:stderr捕获标准错误

你可以使用一个宝石,叫做Frontkick

Frontkick.exec("echo *")

下面是如何检查和阅读它:

result = Frontkick.exec("echo *")


puts result.successful? #=> true if exit_code is 0
puts result.success?    #=> alias to successful?, for compatibility with Process::Status
puts result.stdout      #=> stdout output of the command
puts result.stderr      #=> stderr output of the command
puts result.exit_code   #=> exit_code of the command
puts result.status      #=> alias to exit_code
puts result.exitstatus  #=> alias to exit_code, for compatibility with Process::Status
puts result.duration    #=> the time used to execute the command

Github https://github.com/sonots/frontkick#frontkick

Gem Page https://rubygems.org/gems/frontkick