我该如何使用Ruby进行Shell脚本编写?

172

我有一些简单的shell脚本任务想要完成。

比如:从与某个正则表达式匹配的文件列表中选择工作目录下的一个文件。

我知道可以使用标准的bash和grep来完成这样的事情,但如果能够编写可在Windows和Linux上工作的快速脚本,而不需要记住大量命令行程序和标志等信息,那将是很好的。

我尝试过了,但最终还是搞混了一些信息,例如对当前目录的引用等。

因此问题是:我需要了解哪些Ruby库才能编写Ruby shell脚本?


13个回答

152

默认情况下,您已经可以访问DirFile,它们本身就非常有用。

Dir['*.rb'] #basic globs
Dir['**/*.rb'] #** == any depth of directory, including current dir.
#=> array of relative names

File.expand_path('~/file.txt') #=> "/User/mat/file.txt"
File.dirname('dir/file.txt') #=> 'dir'
File.basename('dir/file.txt') #=> 'file.txt'
File.join('a', 'bunch', 'of', 'strings') #=> 'a/bunch/of/strings'

__FILE__ #=> the name of the current file

还有一个非常有用的标准库,叫做FileUtils

require 'fileutils' #I know, no underscore is not ruby-like
include FileUtils
# Gives you access (without prepending by 'FileUtils.') to
cd(dir, options)
cd(dir, options) {|dir| .... }
pwd()
mkdir(dir, options)
mkdir(list, options)
mkdir_p(dir, options)
mkdir_p(list, options)
rmdir(dir, options)
rmdir(list, options)
ln(old, new, options)
ln(list, destdir, options)
ln_s(old, new, options)
ln_s(list, destdir, options)
ln_sf(src, dest, options)
cp(src, dest, options)
cp(list, dir, options)
cp_r(src, dest, options)
cp_r(list, dir, options)
mv(src, dest, options)
mv(list, dir, options)
rm(list, options)
rm_r(list, options)
rm_rf(list, options)
install(src, dest, mode = <src's>, options)
chmod(mode, list, options)
chmod_R(mode, list, options)
chown(user, group, list, options)
chown_R(user, group, list, options)
touch(list, options)

这相当不错


116

如其他人所说,你的第一行应该是:

#!/usr/bin/env ruby

而且你还需要使其可执行:(在shell中)

chmod +x test.rb

接下来是 Ruby 代码。如果你打开一个文件

File.open("file", "r") do |io|
    # do something with io
end

该文件已在当前目录中打开,该目录可以通过shell中的pwd命令获取。

您的脚本路径也很容易获取。使用$0可以获取shell的第一个参数,即您的脚本的相对路径。可以这样确定绝对路径:

#!/usr/bin/env ruby
require 'pathname'
p Pathname.new($0).realpath()

对于文件系统操作,我几乎总是使用Pathname。它是其他许多文件系统相关类的包装器。还有一个有用的类:Dir,File...


68

以下是其他答案中缺失的重要内容:通过全局数组ARGV,命令行参数可暴露给您的Ruby shell脚本。

因此,如果您有一个名为my_shell_script的脚本:

#!/usr/bin/env ruby
puts "I was passed: "
ARGV.each do |value|
  puts value
end

...将其设置为可执行(正如其他人所提到的):

chmod u+x my_shell_script

然后这样调用:

> ./my_shell_script one two three four five

你将得到这个:

I was passed: 
one
two
three
four
five
参数可以很好地与文件名扩展配合使用:
./my_shell_script *

I was passed: 
a_file_in_the_current_directory
another_file    
my_shell_script
the_last_file

这大部分只适用于UNIX(Linux,Mac OS X),但您可以在Windows上进行类似的操作(尽管不太方便)。


32

这里有很多好的建议,所以我想再补充一点。

  1. 反引号(或重音符号)让你更容易地执行一些脚本操作。例如:

    puts `find . | grep -i lib`
    
    如果您使用反引号运行时遇到输出问题,会将内容发送到标准错误输出而非标准输出。请参考此建议
    out = `git status 2>&1`
    
  2. 反引号用于字符串插值:

    blah = 'lib'
    `touch #{blah}`
    
  3. 你也可以在Ruby中使用管道。这是链接到我的博客,但它会链接回来,所以没关系 :) 在这个主题上可能有更高级的东西。

  4. 正如其他人所指出的,如果你想要更加严肃的操作,可以使用Rush:不仅作为Shell替代品(对我来说有点过于夸张),而且还是用于您的Shell脚本和程序的库。


在Mac上,可以在Ruby中使用Applescript获得更多功能。这是我的shell_here脚本:

#!/usr/bin/env ruby
`env | pbcopy` 
cmd =  %Q@tell app "Terminal" to do script "$(paste_env)"@
puts cmd

`osascript -e "${cmd}"`

我只需要再缩进代码4个空格,就可以格式化它们了。我也把反引号放回去了,但我对Ruby并不是很熟悉,所以你最好检查一下确保它是你想要的。 - Bill the Lizard
@Bill the Lizard,是的,那就是我需要的“技巧”:双重缩进。感谢您的帮助。 - Dan Rosenstark
1
我认为 Rush 已经死了:http://groups.google.com/group/ruby-shell/browse_thread/thread/1f856f6960165309# - Dan Rosenstark

21

2
好书,我正在阅读它:感觉就像是一次禅修式的编程之旅。如果你不了解TDD,你也会在路上学习到TDD的基础知识。 - Sébastien RoccaSerra
我认为这本书有一些好的信息,但对于有经验的程序员来说,开销太大了。 - The D Merged

12

这里还有一个有用的链接:http://rush.heroku.com/

我并没有太多使用过,但它看起来非常棒。

从网站上了解到:

Rush是Unix shell(如bash、zsh等)的替代品,它使用纯Ruby语法。在Rush中,你可以像在shell中一样通过文件进行grep操作、查找和停止进程、复制文件等等 - 现在你可以通过Ruby完成所有这些操作了。


2
为什么不赶紧做呢?http://groups.google.com/group/ruby-shell/browse_thread/thread/75c8cea757d2f680# 这很棒,但没有人掌舵。 - Dan Rosenstark

12
假设你写了一个名为 script.rb 的脚本文件。请将其放置于:
#!/usr/bin/env ruby

将以下命令作为第一行,然后执行chmod +x script.rb


7
当你想编写更复杂的 Ruby 脚本时,以下工具可能会有所帮助:
例如:
  • thor(一个脚本框架)

  • gli(类似 git 的界面)

  • methadone(用于创建简单的工具)

它们可以让你快速开始编写自己的脚本,特别是“命令行应用程序”。

6

以上答案对于在使用Ruby作为shell脚本时非常有趣和有用。但是对我而言,我并不将Ruby作为我的日常编程语言,我更喜欢将Ruby仅作为流程控制使用,并仍然使用bash来完成任务。

一些辅助函数可以用于测试执行结果。

#!/usr/bin/env ruby
module ShellHelper
  def test(command)
    `#{command} 2> /dev/null`
    $?.success?
  end

  def execute(command, raise_on_error = true)
    result = `#{command}`
    raise "execute command failed\n" if (not $?.success?) and raise_on_error
    return $?.success?
  end

  def print_exit(message)
    print "#{message}\n"
    exit
  end

  module_function :execute, :print_exit, :test
end

使用Helper,Ruby脚本可以类似于Bash:
#!/usr/bin/env ruby
require './shell_helper'
include ShellHelper

print_exit "config already exists" if test "ls config"

things.each do |thing|
  next if not test "ls #{thing}/config"
  execute "cp -fr #{thing}/config_template config/#{thing}"
end

5
“如何编写 Ruby” 超出了 SO 的范围。
但是,要将这些 Ruby 脚本转换为可执行脚本,请将以下内容作为您的 Ruby 脚本的第一行:
#!/path/to/ruby

然后使文件可执行:

chmod a+x myscript.rb

然后你就可以开始了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接