另一个选择:
我如何从Ruby程序内部调用shell命令?然后如何将这些命令的输出返回到Ruby?
此说明基于我的一个朋友的注释Ruby script。如果您想改善脚本,请随时在链接上进行更新。
首先,请注意,当Ruby调用shell时,通常会调用/bin/sh
,not Bash。 /bin/sh
在所有系统上均不支持某些Bash语法。
以下是执行Shell脚本的方法:
cmd = "echo 'hi'" # Sample string that can be used
Kernel#`
,通常称为反引号– `cmd`
这与许多其他语言一样,包括Bash,PHP和Perl。
返回shell命令的结果(即标准输出)。
文档:http://ruby-doc.org/core/Kernel.html#method-i-60
value = `echo 'hi'`
value = `#{cmd}`
内置语法,%x( cmd )
[x
字符后是分隔符,可以是任何字符。如果定界符是字符(
,[
,{
或<
之一,文字包含直到匹配的结束定界符的字符,考虑嵌套的定界符对。对于所有其他定界符,文字包含直到下一次出现的字符分隔符。允许使用字符串插补#{ ... }
。
返回shell命令的结果(即标准输出),就像反引号一样。
文档:http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html
value = %x( echo 'hi' )
value = %x[ #{cmd} ]
Kernel#system
在子shell中执行给定命令。
如果找到命令并成功运行,则返回true
,否则返回false
。
文档:http://ruby-doc.org/core/Kernel.html#method-i-system
wasGood = system( "echo 'hi'" )
wasGood = system( cmd )
Kernel#exec
通过运行给定的外部命令替换当前进程。
不返回任何值,当前进程将被替换并且永远不会继续。
文档: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
更多阅读,请参阅:
如果您真的需要Bash,请按照“最佳”答案中的注释。
您还可以使用反引号运算符(`),类似于Perl:
我们可以通过多种方式实现它。
最简单的方法是,例如:
使用此处的答案并链接到Mihai的答案中,我建立了一个满足这些要求的功能:
不要忘记spawn
命令来创建执行指定命令的后台进程。您甚至可以使用Process
类和返回的pid
来等待其完成:
pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid
pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid
如果您的案例比普通案例更复杂,并且无法使用``
处理,请签出Kernel.spawn()
。这似乎是普通Ruby提供的用于执行外部命令的最通用/最全面的功能。
您可以使用它来:
backticks(`)方法是从Ruby调用shell命令的最简单方法。它返回shell命令的结果:
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)
给出类似 url_request = 'http://google.com'
result_of_shell_command = `curl #{url_request}`
的命令:
attrib
这不是真正的答案,但也许有人会发现它有用:
[在Windows上使用TK GUI时,您需要从rubyw调用shell命令,您总是会在不到一秒钟的时间内弹出一个烦人的CMD窗口。
为避免这种情况,您可以使用:
[这是我在OS X上的ruby脚本中使用的一个很酷的脚本(这样,即使从窗口切换后,我也可以启动脚本并获得更新):
spawn()
我喜欢这样做的方式是使用%x
文字,这使得在命令中使用引号变得容易(并且易于阅读!),如下所示:
directorylist = %x[find . -name '*test.rb' | sort]
在这种情况下,将使用当前目录下的所有测试文件填充文件列表,您可以按预期进行处理:
directorylist.each do |filename|
filename.chomp!
# work with file
end
这是我认为有关在Ruby中运行Shell脚本的最佳文章:“ 6 Ways to Run Shell Commands in Ruby”。
如果仅需要获取输出,请使用反引号。
我需要更高级的东西,例如STDOUT和STDERR,所以我使用了Open4 gem。您已在此处说明了所有方法。
我的最爱是Open3
require "open3"
Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }
在这些机制之间进行选择时要考虑的一些事情是:
您可能需要从简单的反引号(``),system()
和IO.popen
到带有Kernel.fork
和Kernel.exec
的成熟的IO.pipe
/ IO.select
。
[如果子流程执行时间太长,您可能还想将超时投入混合。
不幸的是,非常取决于
]。另一个选择:
当您:
您可以使用shell重定向:
puts %x[cat bogus.txt].inspect
=> ""
puts %x[cat bogus.txt 2>&1].inspect
=> "cat: bogus.txt: No such file or directory\n"
我绝对不是Ruby专家,但我会给您一个机会:
$ irb
system "echo Hi"
Hi
=> true
您还应该能够执行以下操作:
cmd = 'ls'
system(cmd)
上面的答案已经很不错了,但是我真的很想分享以下摘要文章:“ 6 Ways to Run Shell Commands in Ruby”
基本上,它告诉我们:
Kernel#exec
:
exec 'echo "hello $HOSTNAME"'
[
system
和$?
:system 'false' puts $?
反引号(`):
today = `date`
IO#popen
:IO.popen("date") { |f| puts f.gets }
Open3#popen3
-stdlib:require "open3" stdin, stdout, stderr = Open3.popen3('dc')
Open4#popen4
-宝石:require "open4" pid, stdin, stdout, stderr = Open4::popen4 "false" # => [26327, #<IO:0x6dff24>, #<IO:0x6dfee8>, #<IO:0x6dfe84>]
如果您真的需要Bash,请按照“最佳”答案中的注释。
首先,请注意,当Ruby调用shell时,通常会调用
/bin/sh
,not
/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 should
尊重shebang,但您可以始终使用
system("bash ./my_script.sh")
为确保,尽管
/bin/sh
运行/bin/bash
可能会有一点开销,但您可能不会注意到。
您还可以使用反引号运算符(`),类似于Perl:
directoryListing = `ls /`
puts directoryListing # prints the contents of the root directory
方便,如果您需要简单的东西。
您想使用哪种方法取决于您要完成的工作;检查文档以获取有关不同方法的更多详细信息。
我们可以通过多种方式实现它。
使用Kernel#exec
,此命令执行后什么都没有:
exec('ls ~')
使用
backticks or %x
`ls ~` => "Applications\nDesktop\nDocuments" %x(ls ~) => "Applications\nDesktop\nDocuments"
使用
Kernel#system
命令,如果成功则返回true
,如果不成功则返回false
,如果命令执行失败则返回nil
:system('ls ~') => true
最简单的方法是,例如:
reboot = `init 6`
puts reboot
使用此处的答案并链接到Mihai的答案中,我建立了一个满足这些要求的功能:
作为奖励,如果shell命令成功退出(0)并将任何内容放在STDOUT上,则此命令也将返回STDOUT。以这种方式,它不同于system
,在这种情况下,它仅返回true
。
代码如下。具体功能是system_quietly
:
require 'open3'
class ShellError < StandardError; end
#actual function:
def system_quietly(*cmd)
exit_status=nil
err=nil
out=nil
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
err = stderr.gets(nil)
out = stdout.gets(nil)
[stdin, stdout, stderr].each{|stream| stream.send('close')}
exit_status = wait_thread.value
end
if exit_status.to_i > 0
err = err.chomp if err
raise ShellError, err
elsif out
return out.chomp
else
return true
end
end
#calling it:
begin
puts system_quietly('which', 'ruby')
rescue ShellError
abort "Looks like you don't have the `ruby` command. Odd."
end
#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"
不要忘记spawn
命令来创建执行指定命令的后台进程。您甚至可以使用Process
类和返回的pid
来等待其完成:
pid = spawn("tar xf ruby-2.0.0-p195.tar.bz2")
Process.wait pid
pid = spawn(RbConfig.ruby, "-eputs'Hello, world!'")
Process.wait pid
文档说:此方法类似于#system
,但它不等待命令完成。
如果您的案例比普通案例更复杂,并且无法使用``
处理,请签出Kernel.spawn()
。这似乎是普通Ruby提供的用于执行外部命令的最通用/最全面的功能。
您可以使用它来:
Kernel.spawn()
有足够好的示例:
Ruby documentationbackticks(`)方法是从Ruby调用shell命令的最简单方法。它返回shell命令的结果:
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)
给出类似
url_request = 'http://google.com' result_of_shell_command = `curl #{url_request}`
的命令:attrib
我发现虽然这种方法不如]难忘>
require 'open3' a="attrib" Open3.popen3(a) do |stdin, stdout, stderr| puts stdout.read end
或
system("thecommand")
反引号中,与其他方法相比,该方法的优点是反引号似乎不让我
`thecommand`
我运行的命令/将要运行的命令存储在变量中,并且puts
似乎不让我获取输出,而此方法可以让我完成这两个操作东西,它使我可以独立访问stdin,stdout和stderr。请参见“
system("thecommand")
”和Executing commands in ruby。
这不是真正的答案,但也许有人会发现它有用:
[在Windows上使用TK GUI时,您需要从rubyw调用shell命令,您总是会在不到一秒钟的时间内弹出一个烦人的CMD窗口。
为避免这种情况,您可以使用:
Ruby's Open3 documentation
或
WIN32OLE.new('Shell.Application').ShellExecute('ipconfig > log.txt','','','open',0)
两者都将
WIN32OLE.new('WScript.Shell').Run('ipconfig > log.txt',0,0)
输出存储在ipconfig
内,但是没有窗口出现。您将需要在脚本中输入
log.txt
。
[require 'win32ole'
,system()
和exec()
将在使用TK和rubyw时都弹出该烦人的窗口。
[这是我在OS X上的ruby脚本中使用的一个很酷的脚本(这样,即使从窗口切换后,我也可以启动脚本并获得更新):
spawn()
另一个选择:
我绝对不是Ruby专家,但我会给您一个机会:
上面的答案已经很不错了,但是我真的很想分享以下摘要文章:“ 6 Ways to Run Shell Commands in Ruby”