Hi all,
似乎 Ruby 没有实现过完整的 exec 系统调用?而且就算是 Kernel.exec 也和 UNIX 系统调用的参数不一样,前者更像是普通的 shell 命令。 有没有办法在 Ruby 上实现完整的 UNIX exec 系统调用呢? 不要用 Gem 实现哦?
谢谢大家
ruby 的 Kernel
是 vm kernel 不是 system kernel
用标准库的 fiddle 就可以了,例如映射 execv
. 需要点 C 的基础才能看懂...
require "fiddle"
require "fiddle/cparser"
module SysFunc
include Fiddle
extend Fiddle::CParser
# note: only very basic types can be used
name, ret_type, arg_type = parse_signature 'int execv(char*, void*)'
func = Function.new Handle.new[name], arg_type, ret_type
define_singleton_method :execv do |path, *args|
# ruby string to char*
args.map!{|s| Pointer[s.to_s].to_i }
# add NULL to terminate
args << 0
# make array of pointers
args_c_array = args.pack(SIZEOF_VOIDP == 8 ? 'Q*' : 'L*')
func.call path.to_s, args_c_array
end
end
SysFunc.execv '/bin/ls', '.'
就是 fiddle 文档很少,用 ffi gem 的话会容易写一点...
fiddle 可以看源代码和 ruby 源代码目录下的 test/fiddle ...
#6 楼 @bhuztez 这得先装一个 gem: rubypython, 结果是通过 FFI 调用 python 去调用一个包装了 FFI 的库来达到 FFI 的目的...
不知道楼主说的参数调用是什么意思,ruby 里的 exec 相关命令都可以直接字符串和数组做参数的:
[1] pry(main)> exec "ls", "-lh", "."
total 3627464
#10 楼 @bhuztez http://docs.python.org/2/library/subprocess.html 这东西和 Ruby 的 system/exec/popen 有啥区别?
This module intends to replace several other, older modules and functions, such as:
os.system
os.spawn*
os.popen*
popen2.*
commands.*
不知道楼主说的参数调用是什么意思,ruby 里的 exec 相关命令都可以直接字符串和数组做参数的
假设有这样一个 C 程序:
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
for(i = 0; i < argc; ++i) {
printf("%s ", argv[i]);
}
printf("\n");
return 0;
}
然后有这样的 C 程序去执行它:
#include <unistd.h>
int main(int argc, char *argv[]) {
execl("output", "param1", "param2", "param3", 0);
return 0;
}
结果是
param1 param2 param3
但是如果改用 Ruby 编写代码去执行:
exec './output', 'param1', 'param2', 'param3'
结果理论上是一样的,但是事实上不同:
./output param1 param2 param3
可以看到被调用的程序收到 4 个参数。双方在第一个参数上是不同的。
#16 楼 @iBachue 四个参数只是表象..perform_exec
的时候就是最终的状态了。
[4] pry(main)> $ Rubinius::Spawn.exec
From: /Users/hooopo/.rvm/rubies/rbx-head/kernel/common/process19.rb @ line 243:
Owner: #<Class:Rubinius::Spawn>
Visibility: public
Number of lines: 6
def self.exec(env, prog, argv, redirects, options)
setup_redirects(redirects)
setup_options(options)
ENV.update(env)
Process.perform_exec prog, argv
end
output=`dmesg | grep hda`
# becomes
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]
这招 Ruby 搞起来就很困难,只能用set -o pipefail;
IO.pipe do |r1, w1|
spawn "dmesg", :out => w1
w1.close
IO.pipe do |r2, w2|
spawn "grep", "hda", :in => r1, :out => w2
w2.close
r2.read
end
end
@hooopo 如果用封装了 spawn 和 pipe 的 popen, 就差不多了:
pipe = IO.popen 'dmesg'
pipe = IO.popen ['grep', 'hda'], in: pipe
pipe.read
#30 楼 @luikore http://docs.python.org/2/library/subprocess.html#replacing-older-functions-with-the-subprocess-module
这里说的是:
output=`dmesg | grep hda` # 以前的这种写法如果前面的管道fail掉,结果还会正常返回,会Silent Failures
# becomes
output=check_output("dmesg | grep hda", shell=True) #用subprocess之后同样写法会Fail Fast。
也就是这篇里提到的一个问题:http://julialang.org/blog/2012/03/shelling-out-sucks/
注意是还使用 shell 的管道写法,不是用程序自己拼接管道。