Ruby Ruby 有没有实现完整的 exec 系列系统调用?

ibachue · 2013年05月23日 · 最后由 bhuztez 回复于 2013年05月24日 · 5628 次阅读

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 ...

#2 楼 @luikore 还能这样用啊 这段代码会被编译成 C?

#3 楼 @lostleaf 不编译的,这段代码只是做了 ruby 和 c 之间的数据转换,相当于 adapter

#1 楼 @luikore 好吧 真心感谢

import subprocess to rescue

7 楼 已删除

#6 楼 @bhuztez 这得先装一个 gem: rubypython, 结果是通过 FFI 调用 python 去调用一个包装了 FFI 的库来达到 FFI 的目的...

不知道楼主说的参数调用是什么意思,ruby 里的 exec 相关命令都可以直接字符串和数组做参数的:

[1] pry(main)> exec "ls", "-lh", "."
total 3627464

#9 楼 @hooopo

exec[lv]p?e? 8 种

#10 楼 @bhuztez http://docs.python.org/2/library/subprocess.html 这东西和 Ruby 的 system/exec/popen 有啥区别?

#11 楼 @hooopo

This module intends to replace several other, older modules and functions, such as:

os.system
os.spawn*
os.popen*
popen2.*
commands.*

#10 楼 @bhuztez 加上 execvP 是 7 种,不过一个 execve 就够了

其实楼主可能不知道 ruby 的 exec 可以带个 :env 选项才问这个问题的...

#11 楼 @hooopo subprocess 会创建新进程,Ruby 的那几个,其中 exec 不会创建新进程。

#9 楼 @hooopo

不知道楼主说的参数调用是什么意思,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 个参数。双方在第一个参数上是不同的。

#15 楼 @hooopo 嗯 没用过 py 的 subprocess,去看看。

#13 楼 @luikore 真不知道 求教

#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

#19 楼 @hooopo 好吧 那这个 Process.perform_exec 我该如何调用呢?

#18 楼 @iBachue 好吧我猜错了不是 env 的问题,ri exec 可以看到各种选项

你要的是这个...

exec ['./output', 'param1'], 'param2', 'param3'

#21 楼 @luikore 额 好吧 原来第一个参数竟然是个数组。。

#22 楼 @iBachue ruby 是解决了 execve 的坑之一:argv[0] 竟然可以和可执行文件的名字不一样

你在 bash 下面 ./output param1 param2 param3 效果也是和 ruby 的 exec 一样的

想踩回那个坑的话也可以,看文档...

#21 楼 @luikore 还是ri厉害!

#24 楼 @hooopo #23 楼 @luikore 我 ri 貌似工作不正常(可能没装这部分文档)不过还是在 ruby-doc 上看到文档了 谢谢二位

@hooopo subprocess 就是和 spawn 差不多的库吧,没有 spawn 好使的样子...

#26 楼 @luikore spawn gem 还是 Ruby 自带的spawn?貌似 spawn 这个 gem 因为 Ruby 自带了 spawn 然后改名成 spawnling 了。

#27 楼 @hooopo 自带的 Process.spawn 啊,看完选项各种 popen3 popen4 都不需要了...

#26 楼 @luikore

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;

#29 楼 @hooopo

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 的管道写法,不是用程序自己拼接管道。

#32 楼 @hooopo 嗯... 出错是个问题,pipe 的话也还要在 popen 后加上 $? 的判断...

#23 楼 @luikore 当然可以不一样了,想不一样就不一样,busybox 就是类似这样耍赖的

#32 楼 @hooopo shell=True了你接个管道就不会了,和 Ruby shelling out 应该一样的结果 ...

需要 登录 后方可回复, 如果你还没有账号请 注册新账号