如何使用Ruby进行shell脚本编写?

How do I use Ruby for shell scripting?

我有一些简单的shell脚本编写任务,我想做

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

我知道我可以使用标准的bash和grep做这种事情,但我很高兴能够破解可以在windows和linux中运行的快速脚本,而不必记住一堆命令行程序和标志等。

我试图实现这一目标,但最终对我应该获取信息的地方感到困惑,例如对当前目录的引用

所以问题是编写ruby shell脚本需要知道Ruby库的哪些部分?


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

1
2
3
4
5
6
7
8
9
10
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

从stdlib中也很有用的是FileUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)

这很不错


正如其他人已经说过的那样,你的第一行应该是

1
#!/usr/bin/env ruby

而且你还必须使它可执行:(在shell中)

1
chmod +x test.rb

然后遵循ruby代码。如果你打开一个文件

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

该文件在shell中使用pwd获取的当前目??录中打开。

脚本的路径也很简单。使用$0,您将获得shell的第一个参数,它是脚本的相对路径。绝对路径可以这样确定:

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

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


以下是其他答案中缺少的重要内容:命令行参数通过ARGV(全局)数组公开给您的Ruby shell脚本。

所以,如果你有一个名为my_shell_script的脚本:

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

...让它可执行(正如其他人提到的):

1
chmod u+x my_shell_script

并称之为:

1
> ./my_shell_script one two three four five

你会得到这个:

1
2
3
4
5
6
I was passed:
one
two
three
four
five

参数与文件名扩展很好地配合:

1
2
3
4
5
6
7
./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中执行类似(虽然不太方便)的操作。


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

  • 反引号(或反向标记)可以让你更轻松地编写脚本。考虑

    1
    puts `find . | grep -i lib`
  • 如果你在获得反引号的输出时遇到问题,那么这些东西将是标准错误而不是标准输出。使用这个建议

    1
    out = `git status 2>&1`
  • 反引号进行字符串插值:

    1
    2
    blah = 'lib'
    `touch #{blah}`
  • 你也可以在Ruby里面管道。它是我的博客的链接,但它链接回来,所以没关系:)这个主题可能有更多高级的东西。

  • 正如其他人所指出的那样,如果你想认真对待Rush:不仅仅是作为shell替换(对我来说有点太过分了),而且还作为一个用于shell脚本和程序的库。

  • 在Mac上,使用Ruby内部的Applescript获得更多功能。这是我的shell_here脚本:

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

    `osascript -e"${cmd}"`


    使用Ruby获取Everyday Scripting的副本。它有很多关于如何做你想要做的事情的有用提示。


    这可能也有帮助:http://rush.heroku.com/

    我没有用太多,但看起来很酷

    从网站:

    rush is a replacement for the unix shell (bash, zsh, etc) which uses pure Ruby syntax. Grep through files, find and kill processes, copy files - everything you do in the shell, now in Ruby


    假设您编写了script.rb脚本。放:

    1
    #!/usr/bin/env ruby

    作为第一行并做一个chmod +x script.rb


    当您想编写更复杂的ruby脚本时,这些工具可能有所帮助:

    例如:

    • thor(脚本框架)

    • gli(git like interface)

    • 美沙酮(用于创建简单的工具)

    它们使您可以快速开始编写自己的脚本,尤其是"命令行应用程序"。


    当使用Ruby作为shell脚本时,上面的答案很有趣并且非常有用。 对我来说,我不使用Ruby作为我的日常语言,我更喜欢使用ruby作为流量控制而仍然使用bash来完成任务。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #!/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
    "
    if (not $?.success?) and raise_on_error
        return $?.success?
      end

      def print_exit(message)
        print"#{message}
    "

        exit
      end

      module_function :execute, :print_exit, :test
    end

    使用帮助程序,ruby脚本可能是bash相似的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #!/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

    将它放在script.rb的开头

    1
    #!/usr/bin/env ruby

    然后将其标记为可执行:

    1
    chmod +x script.rb

    "我怎么写ruby"有点超出了SO的范围。

    但是要将这些ruby脚本转换为可执行脚本,请将其作为ruby脚本的第一行:

    1
    #!/path/to/ruby

    然后使文件可执行:

    1
    chmod a+x myscript.rb

    然后你走吧


    网络摄像头的答案是完美的。我只想指出你的补充。如果必须为脚本处理命令行参数很多,则应使用optparse。它很简单,可以帮助您。


    在ruby中,常量__FILE__将始终为您提供正在运行的脚本的路径。

    在Linux上,/usr/bin/env是你的朋友:

    1
    2
    3
    4
    #! /usr/bin/env ruby
    # Extension of this script does not matter as long
    # as it is executable (chmod +x)
    puts File.expand_path(__FILE__)

    在Windows上,它取决于.rb文件是否与ruby相关联。
    如果它们是:

    1
    2
    # This script filename must end with .rb
    puts File.expand_path(__FILE__)

    如果不是,你必须在它们上显式调用ruby,我使用一个中间的.cmd文件:

    my_script.cmd:

    1
    @ruby %~dp0\my_script.rb

    my_script.rb:

    1
    puts File.expand_path(__FILE__)