Передайте змінні в сценарій Ruby через командний рядок


275

Я встановив RubyInstaller в Windows, і я запускаю 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

Відповіді:


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/…
Sander Mertens

3
Чи не "test1 test2" є лише одним аргументом?
wuliwong

Вам потрібно додати #!/usr/bin/env rubyповерх .rbфайлу, щоб мати змогу запустити його так:./test.rb
xamenrax

191

Не винаходити колесо; ознайомтеся з класною бібліотекою OptionParser Ruby .

Він пропонує розбір прапорів / комутаторів, параметрів з необов’язковими або необхідними значеннями, може розбирати списки параметрів в один варіант і може генерувати вашу допомогу для вас.

Крім того, якщо будь-яка інформація, що передається, є досить статичною, вона не змінюється між прогонами, введіть її у файл 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)

2
Посилання OptParse мертве. Спробуйте ruby-doc.org/stdlib-1.9.3/libdoc/optparse/rdoc/…
Кейсі

7
Відмінна відповідь; Можливо, варто додати, що після аналізу синтаксису опцій, ARGVмістяться лише операнди, якщо такі є (тобто решта аргументів NON-опціону).
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змінної.

Очевидним недоліком є ​​те, що ти залежить від порядку значень.

Якщо вам потрібні лише булеві комутатори, скористайтеся опцією -sінтерпретатора Ruby:

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

Зверніть увагу на --перемикач, інакше Ruby поскаржиться на неіснуючий варіант -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

Тут є недоліки, вам потрібно встановити всі змінні перед викликом сценарію (лише для вашого рубінового процесу) або експортувати їх (оболонки типу BASH):

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

В останньому випадку ваші дані будуть читатими для всіх в одному сеансі оболонки та для всіх підпроцесів, що може бути серйозним наслідком для безпеки.

І принаймні ви можете реалізувати параметр аналізу за допомогою getoptlong та optparse .

Щасливий злом!


1

Ви також можете спробувати cliqr. Його досить новий і в активному розвитку. Але є стабільні випуски, готові до використання. Ось git repo: https://github.com/anshulverma/cliqr

Загляньте в папку з прикладом, щоб отримати уявлення про те, як її можна використовувати.


0

Запустіть цей код у командному рядку та введіть значення N:

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

0

Якщо це не самий тривіальний випадок, у 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. Цей самоцвіт робить ваш чистий код 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

тл; д-р

Я знаю, що це старе, але 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

Що ОП намагається зробити

У цьому випадку я думаю, що найкращим варіантом є використання файлів YAML, як запропоновано у цій відповіді


0

Як @Yunnosch обережно запитав, я детально розробив свою пропозицію.

Аргументи, передані сценарію, зберігаються в ARGV

# Calling my script from current directory with 3 arguments
./my_script username@example.com mail.exmaple.com 143

puts ARGV.inspect                  
#=> ['username@example.com', 'mail.example.com', '143']

За допомогою прапорів усуньте необхідність позиційних аргументів

./my_script --port 143 -h mail.example.com -n username@example.com 

Але тепер нам потрібно отримати значення на основі прапорів. З Ruby це можна зробити в 1 рядок:

SOURCE_HOST = ARGV[ARGV.index { |e| e =~ /-h|--source-host/ } + 1] rescue nil || 'default_source_host'

Давайте деконструюємо синтаксис:

# value should be somewhere inside ARGV (Array)
ARGV[]

# "somewhere" is actually right after the flag. 
# We pass a block to :index to check if the elements from ARGV match our flag. 
ARGV.index { |e| e =~ /-h|--source-host/ } + 1

# If nothing match, rescue nil (instead of raising error from nil + 1)
rescue nil

# Use OR || operator to set default value
|| 'default_source_host'


# Returns the value after the index of the element which match '-h'
# or '--source-host'; or set the default value to 'default_source_host'
ARGV[ARGV.index { |e| e =~ /-h|--source-host/ } + 1] rescue nil || 'default_host'

Хоча якщо це не підходяще рішення, я радий дізнатися більше про крайові шафи


Так, що з цим? Це рішення, яке ви пропонуєте? Тоді поясніть, будь ласка. Це щось із чим у вас є? Потім поясніть це будь-ласка, але замість відповіді на запитання. Це щось, що потрібно знати, перш ніж мати можливість відповісти? Тоді скористайтеся видаленням, будь ласка, і замість цього скористайтеся привілеєм для коментування.
Yunnosch
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.