当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《在Ruby中获取system()调用的输出》经验,为你挑选了13个好方法。

如果我在Ruby中使用Kernel#system调用命令,我该如何获得它的输出?

system("ls")

Craig Walker.. 340

我想扩大和澄清一下混乱的答案.

如果用反引号包围命令,则根本不需要(显式地)调用system().反引号执行命令并将输出作为字符串返回.然后,您可以将值分配给变量,如下所示:

output = `ls`
p output

要么

printf output # escapes newline chars

您可以像使用常规字符串一样进行表达式求值:`ls#{filename}`. (47认同)

这个答案是不可取的:它引入了未经过用户输入的新问题. (35认同)

如果你想要限制stderr,只需在你的命令结束时输入2>&1.例如output =`command 2>&1` (19认同)

如果我需要在命令中提供变量怎么办?也就是说,当使用反引号时,系统("ls"+ filename)会变成什么? (4认同)

@Dogweather:这可能是真的,但它与任何其他方法有什么不同? (4认同)

@Dogweather它只引入未经过清理的用户输入,如果使用了尚未清理过的用户输入的变量.经验丰富的Ruby程序员会小心标记相关的变量(例如`filename_sane`或`filename_escd`),并且只在危险的上下文中使用它们. (2认同)


Simon Hürlim.. 239

要知道,在那里你传递一个字符串包含用户提供的值到所有的解决方案system,%x[]等等都是不安全的!不安全实际上意味着:用户可以触发代码在上下文中运行并具有程序的所有权限.

至于我能说的只有systemOpen3.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命令或系统调用



1> Craig Walker..:

我想扩大和澄清一下混乱的答案.

如果用反引号包围命令,则根本不需要(显式地)调用system().反引号执行命令并将输出作为字符串返回.然后,您可以将值分配给变量,如下所示:

output = `ls`
p output

要么

printf output # escapes newline chars


您可以像使用常规字符串一样进行表达式求值:`ls#{filename}`.
这个答案是不可取的:它引入了未经过用户输入的新问题.
如果你想要限制stderr,只需在你的命令结束时输入2>&1.例如output =`command 2>&1`
如果我需要在命令中提供变量怎么办?也就是说,当使用反引号时,系统("ls"+ filename)会变成什么?
@Dogweather:这可能是真的,但它与任何其他方法有什么不同?
@Dogweather它只引入未经过清理的用户输入,如果使用了尚未清理过的用户输入的变量.经验丰富的Ruby程序员会小心标记相关的变量(例如`filename_sane`或`filename_escd`),并且只在危险的上下文中使用它们.

2> Simon Hürlim..:

要知道,在那里你传递一个字符串包含用户提供的值到所有的解决方案system,%x[]等等都是不安全的!不安全实际上意味着:用户可以触发代码在上下文中运行并具有程序的所有权限.

至于我能说的只有systemOpen3.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命令或系统调用


这是实际回答问题并在不引入新问题(未经过输入的输入)的情况下解决问题的唯一答案.
stdin,stdout和stderr应该[以非块形式明确关闭](http://www.ruby-doc.org/stdlib-2.0/libdoc/open3/rdoc/Open3.html#method-c-popen3).
谢谢!这就是我所希望的答案。一项更正:gets调用应传递参数nil,否则我们将仅获得输出的第一行。例如`stdout.gets(nil)`。

3> 小智..:

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

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


感谢格雷格·普莱斯了解有关逃避用户输入的必要性,但是说这样写的答案是不安全的是不正确的.提到的Open3方法更复杂,并且引入了更多的依赖关系,并且有人将"将其用于非常量后续"的论点是一个稻草人.没错,你可能不会在Rails应用程序中使用它们,但是对于一个简单的系统实用程序脚本而不可能不受信任的用户输入,反引号是完全正常的,任何人都不应该对使用它们感到不好.
这只捕获stdout,stderr进入控制台.要获得stderr,请使用:`output = \`ls no_existing_file 2>&1 \`; 结果= $?成功了吗?'
这个答案是**不安全**并且不应该使用 - 如果命令不是常量,那么反引号语法可能会导致错误,可能是安全漏洞.(即使它是一个常数,它可能会导致某人以后使用它作为非常数并导致错误.)参见[SimonHürlimann的回答](http://stackoverflow.com/a/5970819/378130)为了正确的解决方案.
这正是我所寻找的.谢谢.

4> Denis de Ber..:

直截了当的方式这样做正确,安全地是使用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.capture2,capture2ecapture3工作一样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')


这是正确的答案.它也是最丰富的信息.唯一缺少的是关闭std*s的警告.见[这个其他评论](http://stackoverflow.com/a/21071595/213191):```require'open3'; output = Open3.popen3("ls"){| stdin,stdout,stderr,wait_thr | stdout.read}```注意块形式将自动关闭stdin,stdout和stderr-否则它们必须[显式关闭](http://www.ruby-doc.org/stdlib-2.0/ libdoc/open3 /的RDoc/Open3.html#方法-C-popen3).

5> 小智..:

您可以使用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

个博客发布者周杰伦字段详细解释了使用系统,exec和%×[..]之间的差异.


感谢您使用%x []的提示.它刚刚解决了我在Mac OS X中使用ruby脚本中的后退滴答的问题.在使用Cygwin的Windows机器上运行相同的脚本时,由于后退滴答而失败,但是使用了%x [].

6> Lri..:

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



7> chaos..:

你使用反引号:

`ls`


反引号不会在终端产生输出.
它不会产生stderr,但它会产生stdout.

8> dwc..:

另一种方式是:

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

请注意,这是打开"ls"之前的"管道"字符.这也可用于将数据馈送到程序标准输入以及读取其标准输出.



9> Geoff van de..:

我发现如果你需要返回值,以下内容很有用:

result = %x[ls]
puts result

我特别想在我的机器上列出所有Java进程的pid,并使用它:

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



10> Yarin..:

正如SimonHürlimann 已经解释过的那样,Open3比反引号等更安全.

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

请注意,块形式将自动关闭stdin,stdout和stderr-否则必须明确关闭它们.



11> Eric Anderso..:

虽然使用反引号或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

此方法使用临时文件捕获给定块中的任何输出以存储实际数据.用法示例:

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

您可以system使用内部调用的任何内容替换调用system.如果需要,您也可以使用类似的方法捕获stderr.



12> Ashrith..:

如果您希望将输出重定向到使用的文件Kernel#system,您可以修改如下的描述符:

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

system('ls -al', :out => ['/tmp/log', 'a'], :err => ['/tmp/log', 'a'])

对于长时间运行的命令,这将实时存储输出.您还可以使用IO.pipe存储输出,并从Kernel#system重定向它.



13> Antonin Hild..:

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

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

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有