如何从Ruby程序内部调用shell命令?然后如何将这些命令的输出返回到Ruby中?


当前回答

如果你真的需要Bash,请参考“最佳”答案中的注释。

首先,请注意,当Ruby调用shell时,它通常调用/bin/sh,而不是Bash。在所有系统上/bin/sh不支持某些Bash语法。

如果你需要使用Bash,在你想要的调用方法中插入Bash -c "your Bash-only command":

quick_output = system("ls -la")
quick_bash = system("bash -c 'ls -la'")

测试:

system("echo $SHELL")
system('bash -c "echo $SHELL"')

或者如果您正在运行一个现有的脚本文件,如

script_output = system("./my_script.sh")

Ruby应该尊重shebang,但你总是可以使用

system("bash ./my_script.sh")

为了确保这一点,尽管/bin/sh运行/bin/bash可能会有轻微的开销,但您可能不会注意到。

其他回答

如果您有一个比普通情况更复杂的情况,不能用' '来处理,那么请检查Kernel.spawn()。这似乎是Ruby提供的用于执行外部命令的最通用/功能最齐全的程序。

你可以用它来:

创建进程组(Windows)。 重定向进,出,错误文件/彼此。 设置env vars, umask。 执行命令前请更换目录。 设置CPU/数据等资源限制。 在其他答案中做其他选项可以做的所有事情,但需要更多的代码。

Ruby文档中有足够好的例子:

env: hash
  name => val : set the environment variable
  name => nil : unset the environment variable
command...:
  commandline                 : command line string which is passed to the standard shell
  cmdname, arg1, ...          : command name and one or more arguments (no shell)
  [cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)
options: hash
  clearing environment variables:
    :unsetenv_others => true   : clear environment variables except specified by env
    :unsetenv_others => false  : dont clear (default)
  process group:
    :pgroup => true or 0 : make a new process group
    :pgroup => pgid      : join to specified process group
    :pgroup => nil       : dont change the process group (default)
  create new process group: Windows only
    :new_pgroup => true  : the new process is the root process of a new process group
    :new_pgroup => false : dont create a new process group (default)
  resource limit: resourcename is core, cpu, data, etc.  See Process.setrlimit.
    :rlimit_resourcename => limit
    :rlimit_resourcename => [cur_limit, max_limit]
  current directory:
    :chdir => str
  umask:
    :umask => int
  redirection:
    key:
      FD              : single file descriptor in child process
      [FD, FD, ...]   : multiple file descriptor in child process
    value:
      FD                        : redirect to the file descriptor in parent process
      string                    : redirect to file with open(string, "r" or "w")
      [string]                  : redirect to file with open(string, File::RDONLY)
      [string, open_mode]       : redirect to file with open(string, open_mode, 0644)
      [string, open_mode, perm] : redirect to file with open(string, open_mode, perm)
      [:child, FD]              : redirect to the redirected file descriptor
      :close                    : close the file descriptor in child process
    FD is one of follows
      :in     : the file descriptor 0 which is the standard input
      :out    : the file descriptor 1 which is the standard output
      :err    : the file descriptor 2 which is the standard error
      integer : the file descriptor of specified the integer
      io      : the file descriptor specified as io.fileno
  file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not
    :close_others => false : inherit fds (default for system and exec)
    :close_others => true  : dont inherit (default for spawn and IO.popen)

这个解释是基于我的一个朋友写的带注释的Ruby脚本。如果您想改进脚本,请在链接中进行更新。

首先,请注意,当Ruby调用shell时,它通常调用/bin/sh,而不是Bash。在所有系统上/bin/sh不支持某些Bash语法。

以下是执行shell脚本的方法:

cmd = "echo 'hi'" # Sample string that can be used

Kernel#` , commonly called backticks – `cmd` This is like many other languages, including Bash, PHP, and Perl. Returns the result (i.e. standard output) of the shell command. Docs: http://ruby-doc.org/core/Kernel.html#method-i-60 value = `echo 'hi'` value = `#{cmd}` Built-in syntax, %x( cmd ) Following the x character is a delimiter, which can be any character. If the delimiter is one of the characters (, [, {, or <, the literal consists of the characters up to the matching closing delimiter, taking account of nested delimiter pairs. For all other delimiters, the literal comprises the characters up to the next occurrence of the delimiter character. String interpolation #{ ... } is allowed. Returns the result (i.e. standard output) of the shell command, just like the backticks. Docs: https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html#label-Percent+Strings value = %x( echo 'hi' ) value = %x[ #{cmd} ] Kernel#system Executes the given command in a subshell. Returns true if the command was found and run successfully, false otherwise. Docs: http://ruby-doc.org/core/Kernel.html#method-i-system wasGood = system( "echo 'hi'" ) wasGood = system( cmd ) Kernel#exec Replaces the current process by running the given external command. Returns none, the current process is replaced and never continues. Docs: http://ruby-doc.org/core/Kernel.html#method-i-exec exec( "echo 'hi'" ) exec( cmd ) # Note: this will never be reached because of the line above

这里有一些额外的建议: $ ?,与$CHILD_STATUS相同,如果使用反勾号、system()或%x{},则访问最后一个系统执行命令的状态。 然后你可以访问exitstatus和pid属性:

$?.exitstatus

更多阅读请参见:

http://www.elctech.com/blog/i-m-in-ur-commandline-executin-ma-commands http://blog.jayfields.com/2006/06/ruby-kernel-system-exec-and-x.html http://tech.natemurray.com/2007/03/ruby-shell-commands.html

在我看来,这是关于在Ruby中运行shell脚本的最好的文章:“在Ruby中运行shell命令的6种方法”。

如果您只需要获得输出,请使用反勾号。

我需要更高级的东西,比如STDOUT和STDERR,所以我使用了Open4 gem。这里已经解释了所有的方法。

我喜欢的方法是使用%x文字,这使得在命令中使用引号很容易(而且易读!),如下所示:

directorylist = %x[find . -name '*test.rb' | sort]

在这种情况下,它将填充当前目录下的所有测试文件的文件列表,您可以按照预期进行处理:

directorylist.each do |filename|
  filename.chomp!
  # work with file
end

最简单的方法是:

reboot = `init 6`
puts reboot