0

I am using Ruby to execute a code that takes command line arguments. now i trying to use the same program with differnt options so i am putting the options in a file and i want the program to read each line interpret the options and execute the program accordingly.

but i get this error. "C:/Ruby193/lib/ruby/1.9.1/optparse.rb:1348:in block in parse_in_order': undefined methodshift' for "--c execue --query unix --Servername abc123":String (NoMethodError)"

i understand that its reading the file and treating the line as string. but wondering if there is a way to overcome this shift error and treat the line as if it was entered in command prompt. or any better solution.

here is my code.

require 'optparse'
require 'micro-optparse'
# --command execue --query unix command --Servername abc123
f =File.open("list_of_commands.txt", "r")
f.each_line { |line| 
line= line.chomp
#line = "--c execue --query unix --Servername abc123"
#line = eval("\"#{line}\"")
puts line

 options = {}
OptionParser.new do |opts|

  opts.on("-c", "--command result,execue,chart,scpfile", String, "Single command to execute ") do |c|
    options[:comd] = c
  end

  opts.on("-q", "--query remote command, unix command", String, "performs the command on local or remote machine") do |q|
    options[:query] = q
  end

  opts.on("-s", "--servername CHSXEDWDC002 ", String, "server name to execute the command") do |v|
    options[:hname] = v
  end

    opts.on_tail('-h', '--help', 'Show this message') do
    puts opts
     exit
   end

 end.parse!(line)
 p options
 }

the contents of the file are below --c execue --query unix --Servername abc123

i also tried to use micro-optparse but facing same error. any workaround ?

Update: as per suggestion from "@mu is too short" i tried below options. end.parse!("#{Shellwords.shellsplit(line)}") and/or end.parse!(Shellwords.shellsplit(line)). but none of them worked.

i also tried to split the line as array using "line = line.split("\t")" and then end.parse!(line). out put as --c execue --query unix --Servername abc123

but now i get error as block in : invalid option --c execute

Update:#2 looking at the error, the issue is with the wrong parameter(-c. but thanks to user "@mu is too short" for suggesting to use Array.

Update: 3 passing the array only worked for short form of the arguments such as -c but when long form was supplied it failed with invalid argument erorr.

i dont see much documentation on the optparse. i even tried micro-parse but it requres default valuves and its not an option for me :(

2 Answers 2

2

The parse! method wants an array as its argument, not a string. You'll probable want to use Shellwords.shellsplit rather than String#split (or similar hand-rolled method) to convert your line to an array just in case you have to deal with quoting and whatnot. Something like this:

OptionParser.new do |opts|
  #...
end.parse!(Shellwords.shellsplit(line))
Sign up to request clarification or add additional context in comments.

7 Comments

Hi Thanks for your quick reply. i tried these options. end.parse!("#{Shellwords.shellsplit(line)}") and end.parse!(Shellwords.shellsplit(line)). but none of them worked. i also tried to split the line as array using "line = line.split("\t")" and then end.parse!(line). but not i get block in <main>: invalid option --c execute <OptionPArser::InvalidOption>
Your opts.on arguments don't look right for a --c option. Why do you have --Command when you probably mean --command?
the problem has not fully solved, by passing the array i am able to use the short form of the arguments but when passing the long form i get error as "Invalid option: --command execute (OptionParser::InvalidOption). yes i am using the command and not Command
@user2300908: Works fine for me with either -c or --command, is micro-optparse interfering with the normal optparse behavior?
It works only for short form example "-c execue\t-q unix comamnd and more\t-s abc123" and not for "--command execue\t--query unix comamnd and more\t--servername abc123" i get thsi error "invalid option: --command execue (OptionParser::InvalidOption)" i have also removed micro-optparser. by the way i am spliting each line from file using tab delimited like this "line1 = line.split("\t")" and passing that line to parse like this. "end.parse!(line1)" and also i am using it on windows so could not get to use "(Shellwords.shellsplit(line))"
|
2

While you can put your command-line arguments into a file, flags and all, there are better ways to remember configuration settings.

Instead of storing the flags, use a YAML file. YAML is a great data format, that translates easily to Ruby hashes and objects. "Yaml Cookbook" is a very useful page for learning the ins and outs of the format with Ruby. There are YAML parsers for a myriad other languages, making it easy to share the settings, which can be useful as a system grows.

With a little creative code you can use your YAML as the base settings, and let your CLI flags override the stored settings.

If you're not familiar with YAML, it's easy to get a start on the file using something like:

require 'yaml'

data = {
  'command' => %w[result execute chart scpfile],
  'query' => ['remote command', 'unix command'],
  'servername' => 'CHSXEDWHDC002',
}

puts data.to_yaml

Which outputs:

---
command:
- result
- execute
- chart
- scpfile
query:
- remote command
- unix command
servername: CHSXEDWHDC002

Redirect that output to a file ending in .yaml and you're on your way.

To read it back into a script use:

require 'yaml'

data = YAML.load_file('path/to/data.yaml')

A quick round-trip test shows:

require 'yaml'

data = {
  'command' => %w[result execute chart scpfile],
  'query' => ['remote command', 'unix command'],
  'servername' => 'CHSXEDWHDC002',
}

YAML.load(data.to_yaml)

Which looks like:

{"command"=>["result", "execute", "chart", "scpfile"],
"query"=>["remote command", "unix command"],
"servername"=>"CHSXEDWHDC002"}

If you want to have defaults, stored in the YAML file, and override them with command-line flags, read the data from the file then use that resulting object as the base for OptionParse:

require 'optparse'
require 'yaml'

# Note, YAML can deal with symbols as keys, but other languages might not like them.
options = {
  :comd => %w[result execute chart scpfile],
  :query => ['remote command', 'unix command'],
  :hname => 'CHSXEDWHDC002',
}

# we'll overwrite the options variable to pretend we loaded it from a file.
options = YAML.load(options.to_yaml)

OptionParser.new do |opts|

  opts.on("-c", "--Command result,execue,chart,scpfile", String, "Single command to execute ") do |c|
    options[:comd] = c
  end

  opts.on("-q", "--query remote command, unix command", String, "performs the command on local or remote machine") do |q|
    options[:query] = q
  end

  opts.on("-s", "--Servername CHSXEDWHDC002 ", String, "server name to execute the command") do |v|
    options[:hname] = v
  end

  opts.on_tail('-h', '--help', 'Show this message') do
    puts opts
    exit
  end

end.parse!

That's not tested, but we do similar things at work all the time, so save it to a file and poke at it with a stick for a while, and see what you come up with.

2 Comments

I Liked the idea of using the yaml file. will give it a shot.
I think you'll like it. We have some big configurations, used by multiple related applications, allowing us to define common data directories, queue names that all apps have to know about, database hosts, etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.