Truyền biến cho tập lệnh Ruby thông qua dòng lệnh


275

Tôi đã cài đặt RubyInstaller trên Windows và tôi đang chạy IMAP Sync nhưng tôi cần sử dụng nó để đồng bộ hóa hàng trăm tài khoản. Nếu tôi có thể truyền các biến này cho nó thông qua dòng lệnh, tôi có thể tự động hóa toàn bộ quá trình tốt hơn.

# 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
Bạn có thể muốn xem xét chỉnh sửa câu hỏi phổ biến này thành một câu hỏi thực tế .
not2qubit

Câu trả lời:


465

Một cái gì đó như thế này:

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

sau đó

$ ./test.rb "test1 test2"

hoặc là

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

84
Tôi muốn chỉ ra một cách rõ ràng rằng ARGV [0] không trỏ đến tên chương trình, như một số ngôn ngữ khác làm. Để có được tên chương trình, hãy xem stackoverflow.com/questions/4834821/ cấp
Sander Mertens

3
Không phải `" test1 test2 "chỉ là một đối số sao?
wuliwong

Bạn cần thêm #!/usr/bin/env rubyvào đầu .rbtệp để có thể chạy nó như thế này:./test.rb
xamenrax

191

Đừng phát minh lại bánh xe; kiểm tra thư viện OptionParser tuyệt vời của Ruby .

Nó cung cấp phân tích cờ / công tắc, tham số với các giá trị tùy chọn hoặc bắt buộc, có thể phân tích danh sách các tham số thành một tùy chọn duy nhất và có thể tạo trợ giúp cho bạn.

Ngoài ra, nếu bất kỳ thông tin nào của bạn được truyền vào khá tĩnh, điều đó không thay đổi giữa các lần chạy, hãy đặt nó vào một tệp YAML được phân tích cú pháp. Bằng cách đó, bạn có thể có những thứ thay đổi mỗi lần trên dòng lệnh và những thứ thay đổi đôi khi được định cấu hình bên ngoài mã của bạn. Sự phân tách dữ liệu và mã này là tốt cho bảo trì.

Dưới đây là một số mẫu để chơi với:

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']

Đây là tệp YAML mẫu nếu đích đến của bạn khá tĩnh:

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

Điều này sẽ cho phép bạn dễ dàng tạo tệp 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)

2
Liên kết OptPude đã chết. Hãy thử ruby-doc.org/stdlib-1.9.3/libdoc/optparse/rdoc/ mẹo
Casey

7
Câu trả lời tuyệt vời; có thể đáng để thêm rằng sau khi phân tích tùy chọn được thực hiện, ARGVchỉ chứa các toán hạng, nếu có (đó là các đối số tùy chọn NON còn lại).
mkuity0

27

Thật không may, Ruby không hỗ trợ cơ chế vượt qua như AWK:

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

Điều đó có nghĩa là bạn không thể chuyển trực tiếp các giá trị được đặt tên vào tập lệnh của mình.

Sử dụng tùy chọn cmd có thể giúp:

> 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 lưu trữ tất cả các đối số cmd trong ARGVmảng, chính scriptname có thể được ghi lại bằng $PROGRAM_NAMEbiến.

Nhược điểm rõ ràng là bạn phụ thuộc vào thứ tự của các giá trị.

Nếu bạn chỉ cần các công tắc Boolean, hãy sử dụng tùy chọn -strình thông dịch Ruby:

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

Vui lòng lưu ý công --tắc, nếu không, Ruby sẽ phàn nàn về tùy chọn không tồn tại -agreed, vì vậy hãy chuyển nó dưới dạng chuyển đổi sang lệnh gọi cmd của bạn. Bạn không cần nó trong trường hợp sau:

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

Nhược điểm là bạn gây rối với các biến toàn cục và chỉ có các giá trị logic / sai logic.

Bạn có thể truy cập các giá trị từ các biến môi trường:

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

Hạn chế có mặt ở đây, bạn phải đặt tất cả các biến trước khi gọi tập lệnh (chỉ dành cho quy trình ruby ​​của bạn) hoặc xuất chúng (shell như BASH):

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

Trong trường hợp sau, dữ liệu của bạn sẽ có thể đọc được cho mọi người trong cùng một phiên shell và cho tất cả các quy trình con, đây có thể là một hàm ý bảo mật nghiêm trọng.

Và ít nhất bạn có thể thực hiện một trình phân tích cú pháp tùy chọn bằng cách sử dụng getoptlongoptparse .

Chúc mừng hack!


1

Bạn cũng có thể thử cliqr. Nó khá mới và đang phát triển tích cực. Nhưng có những bản phát hành ổn định sẵn sàng để được sử dụng. Đây là repo git: https://github.com/anshulverma/cliqr

Nhìn vào thư mục ví dụ để có ý tưởng về cách sử dụng nó.


0

Chạy mã này trên dòng lệnh và nhập giá trị của N:

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

0

Trừ khi đó là trường hợp tầm thường nhất, chỉ có một cách lành mạnh để sử dụng các tùy chọn dòng lệnh trong Ruby. Nó được gọi là docopt và tài liệu ở đây .

Điều tuyệt vời với nó, đó là sự đơn giản. Tất cả bạn phải làm, là chỉ định văn bản "trợ giúp" cho lệnh của bạn. Những gì bạn viết sau đó sẽ được tự động phân tích cú pháp bởi thư viện ruby độc lập (!).

Từ ví dụ :

#!/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

Đầu ra:

$ ./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>"=>[]}

Thưởng thức!


0

Bạn nên thử đá quý console_runner . Viên ngọc này làm cho mã Ruby thuần của bạn có thể thực thi được từ dòng lệnh. Tất cả những gì bạn cần là thêm các chú thích YARD vào mã của bạn:

# @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

Sau đó chạy nó từ bàn điều khiển:

$ 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

Tôi biết điều này đã cũ, nhưng getoptlong đã không được đề cập ở đây và có lẽ đó là cách tốt nhất để phân tích các đối số dòng lệnh ngày hôm nay.


Phân tích đối số dòng lệnh

Tôi thực sự khuyên bạn nên getoptlong . Nó khá dễ sử dụng và hoạt động như một bùa mê. Dưới đây là một ví dụ được trích xuất từ ​​liên kết ở trên

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

Bạn có thể gọi nó như thế này ruby hello.rb -n 6 --name -- /tmp

Những gì OP đang cố gắng làm

Trong trường hợp này tôi nghĩ tùy chọn tốt nhất là sử dụng các tệp YAML như được đề xuất trong câu trả lời này

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.