通过命令行将变量传递给Ruby脚本


275

我已经在Windows上安装了RubyInstaller,并且正在运行IMAP Sync,但是我需要使用它来同步数百个帐户。如果我可以通过命令行将这些变量传递给它,则可以更好地自动化整个过程。

# Source server connection info.
SOURCE_NAME = 'username@example.com'
SOURCE_HOST = 'mail.example.com'
SOURCE_PORT = 143
SOURCE_SSL  = false
SOURCE_USER = 'username'
SOURCE_PASS = 'password'

# Destination server connection info.
DEST_NAME = 'username@gmail.com'
DEST_HOST = 'imap.gmail.com'
DEST_PORT = 993
DEST_SSL  = true
DEST_USER = 'username@gmail.com'
DEST_PASS = 'password'

1
您可能需要考虑将这个常见问题编辑为实际问题
not2qubit

Answers:


465

像这样:

ARGV.each do|a|
  puts "Argument: #{a}"
end

然后

$ ./test.rb "test1 test2"

要么

v1 = ARGV[0]
v2 = ARGV[1]
puts v1       #prints test1
puts v2       #prints test2

84
我想明确指出ARGV [0]并不像其他一些语言一样指向程序名称。为了让程序名称,请参阅stackoverflow.com/questions/4834821/...
桑德梅尔滕斯

3
““ test1 test2”不只是一个参数吗?
wuliwong

您需要#!/usr/bin/env ruby.rb文件顶部添加才能像这样运行它:./test.rb
xamenrax

191

不要重新发明轮子。查看Ruby的酷方式OptionParser库。

它提供标志/开关,具有可选或必需值的参数的解析,可以将参数列表解析为单个选项并可以为您提供帮助。

另外,如果您传递的任何信息都是静态的,并且在两次运行之间没有变化,则将其放入要解析的YAML文件中。这样,您可以使每次更改的内容在命令行上都可以更改,并且可以在代码外部偶尔配置更改的内容。数据和代码的分离很容易维护。

以下是一些可玩的示例:

require 'optparse'
require 'yaml'

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: example.rb [options]"

  opts.on('-n', '--sourcename NAME', 'Source name') { |v| options[:source_name] = v }
  opts.on('-h', '--sourcehost HOST', 'Source host') { |v| options[:source_host] = v }
  opts.on('-p', '--sourceport PORT', 'Source port') { |v| options[:source_port] = v }

end.parse!

dest_options = YAML.load_file('destination_config.yaml')
puts dest_options['dest_name']

如果您的目的地非常静态,这是一个示例YAML文件:

--- 
dest_name: username@gmail.com
dest_host: imap.gmail.com
dest_port: 993
dest_ssl: true
dest_user: username@gmail.com
dest_pass: password

这将使您轻松生成YAML文件:

require 'yaml'

yaml = {
  'dest_name' => 'username@gmail.com',
  'dest_host' => 'imap.gmail.com',
  'dest_port' => 993,
  'dest_ssl'  => true,
  'dest_user' => 'username@gmail.com',
  'dest_pass' => 'password'
}

puts YAML.dump(yaml)


7
极好的答案;可能值得补充的是,在完成选项解析之后,ARGV仅包含操作数(如果有的话)(即剩余的NON-option参数)。
mklement0

27

不幸的是,Ruby不支持诸如AWK之类的传递机制:

> awk -v a=1 'BEGIN {print a}'
> 1

这意味着您不能将命名值直接传递到脚本中。

使用cmd选项可能会有所帮助:

> ruby script.rb val_0 val_1 val_2

# script.rb
puts ARGV[0] # => val_0
puts ARGV[1] # => val_1
puts ARGV[2] # => val_2

Ruby将所有cmd参数存储在ARGV数组中,可以使用$PROGRAM_NAME变量捕获脚本名本身。

明显的缺点是您依赖值的顺序。

如果只需要布尔开关,请使用-sRuby解释器的选项:

> ruby -s -e 'puts "So do I!" if $agreed' -- -agreed
> So do I!

请注意--开关,否则Ruby会抱怨不存在的option -agreed,因此请将其作为开关传递给您的cmd调用。在以下情况下,您不需要它:

> ruby -s script_with_switches.rb -agreed
> So do I!

缺点是您会混淆全局变量并且仅具有逻辑true / false值。

您可以从环境变量访问值:

> FIRST_NAME='Andy Warhol' ruby -e 'puts ENV["FIRST_NAME"]'
> Andy Warhol

这里存在缺点,您必须在脚本调用之前设置所有变量(仅适用于您的ruby进程)或导出它们(如BASH之类的shell):

> export FIRST_NAME='Andy Warhol'
> ruby -e 'puts ENV["FIRST_NAME"]'

在后一种情况下,同一外壳会话中的每个人和所有子进程都可以读取您的数据,这可能是严重的安全隐患。

至少您可以使用getoptlongoptparse实现选项解析器。

骇客骇客!



0

在命令行上运行以下代码,然后输入N的值:

N  = gets; 1.step(N.to_i, 1) { |i| print "hello world\n" }

0

除非是最简单的情况,否则在Ruby中只有一种明智的方式来使用命令行选项。它称为docopt在此处进行了说明

它的惊人之处在于它的简单性。您所要做的就是为命令指定“帮助”文本。然后,您所写的内容将由独立的(!)ruby库自动解析。

示例

#!/usr/bin/env ruby
require 'docopt.rb'

doc = <<DOCOPT
Usage: #{__FILE__} --help
       #{__FILE__} -v...
       #{__FILE__} go [go]
       #{__FILE__} (--path=<path>)...
       #{__FILE__} <file> <file>

Try: #{__FILE__} -vvvvvvvvvv
     #{__FILE__} go go
     #{__FILE__} --path ./here --path ./there
     #{__FILE__} this.txt that.txt

DOCOPT

begin
  require "pp"
  pp Docopt::docopt(doc)
rescue Docopt::Exit => e
  puts e.message
end

输出:

$ ./counted_example.rb -h
Usage: ./counted_example.rb --help
       ./counted_example.rb -v...
       ./counted_example.rb go [go]
       ./counted_example.rb (--path=<path>)...
       ./counted_example.rb <file> <file>

Try: ./counted_example.rb -vvvvvvvvvv
     ./counted_example.rb go go
     ./counted_example.rb --path ./here --path ./there
     ./counted_example.rb this.txt that.txt

$ ./counted_example.rb something else
{"--help"=>false,
 "-v"=>0,
 "go"=>0,
 "--path"=>[],
 "<file>"=>["something", "else"]}

$ ./counted_example.rb -v
{"--help"=>false, "-v"=>1, "go"=>0, "--path"=>[], "<file>"=>[]}

$ ./counted_example.rb go go
{"--help"=>false, "-v"=>0, "go"=>2, "--path"=>[], "<file>"=>[]}

请享用!


0

您应该尝试console_runner gem。这个gem使您可以从命令行执行纯Ruby代码。您需要做的就是在代码中添加YARD批注:

# @runnable This tool can talk to you. Run it when you are lonely.
#   Written in Ruby.  
class MyClass

    def initialize
      @hello_msg = 'Hello' 
      @bye_msg = 'Good Bye' 
    end

    # @runnable Say 'Hello' to you.
    # @param [String] name Your name
    # @param [Hash] options options
    # @option options [Boolean] :second_meet Have you met before?
    # @option options [String] :prefix Your custom prefix
    def say_hello(name, options = {})
      second_meet = nil
      second_meet = 'Nice to see you again!' if options['second_meet']
      prefix = options['prefix']
      message = @hello_msg + ', '
      message += "#{prefix} " if prefix
      message += "#{name}. "
      message += second_meet if second_meet
      puts message
    end

end

然后从控制台运行它:

$ c_run /projects/example/my_class.rb  say_hello -n John --second-meet --prefix Mr. 
-> Hello, Mr. John. Nice to see you again!

0

tl; dr

我知道这很旧,但是这里没有提到getoptlong,它可能是当今解析命令行参数的最佳方法。


解析命令行参数

我强烈建议getoptlong。它非常易于使用,并且像魅惑一样工作。这是从上面的链接中提取的示例

require 'getoptlong'

opts = GetoptLong.new(
    [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
    [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--name', GetoptLong::OPTIONAL_ARGUMENT ]
)

dir = nil
name = nil
repetitions = 1
opts.each do |opt, arg|
    case opt
        when '--help'
            puts <<-EOF
hello [OPTION] ... DIR

-h, --help:
     show help

--repeat x, -n x:
     repeat x times

--name [name]:
     greet user by name, if name not supplied default is John

DIR: The directory in which to issue the greeting.
            EOF
        when '--repeat'
            repetitions = arg.to_i
        when '--name'
            if arg == ''
                name = 'John'
            else
                name = arg
            end
    end
end

if ARGV.length != 1
    puts "Missing dir argument (try --help)"
    exit 0
end

dir = ARGV.shift

Dir.chdir(dir)
for i in (1..repetitions)
    print "Hello"
    if name
        print ", #{name}"
    end
    puts
end

你可以这样称呼它 ruby hello.rb -n 6 --name -- /tmp

OP正在尝试做什么

在这种情况下,我认为最好的选择是按照此答案的建议使用YAML文件

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.