如果我调用一个命令使用内核#系统在Ruby中,我如何得到它的输出?
system("ls")
如果我调用一个命令使用内核#系统在Ruby中,我如何得到它的输出?
system("ls")
当前回答
正确而安全地做到这一点的简单方法是使用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')
其他回答
您可以使用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
Jay Fields的博客文章详细解释了system、exec和%x[..]]。
我发现下面是有用的,如果你需要返回值:
result = %x[ls]
puts result
我特别想列出我的机器上所有Java进程的pid,并使用以下方法:
ids = %x[ps ax | grep java | awk '{ print $1 }' | xargs]
最方便的方法是:
stdout_str, stderr_str, status = Open3.capture3(cmd)
puts "exit status: #{status.exitstatus} stdout: #{stdout_str}"
将标准输出捕获到名为val的变量中最简单的解决方案:
val = capture(:stdout) do
system("pwd")
end
puts val
缩短版本:
val = capture(:stdout) { system("ls") }
捕获方法由 active_support / core_ext /内核/ reporting.rb
类似地,我们也可以用:stderr捕获标准错误
另一种方法是:
f = open("|ls")
foo = f.read()
注意,这是open中“ls”前面的“pipe”字符。这也可用于将数据送入程序的标准输入以及读取其标准输出。