![]() |
cmdparse - advanced command line parser supporting commands |
Here are some short code fragments which show how to use cmdparse. A complete example app can be found later in the tutorial section.
Defining commands using classes:
class TestCmd < CmdParse::Command
def initialize
super('test', true)
self.add_command(TestSubCmd.new)
end
end
class TestSubCmd < CmdParse::Command
def initialize
super('sub',false)
end
def execute (args)
puts "Hallo #{args}"
end
end
cmd = CmdParse::CommandParser.new( true )
cmd.add_command(TestCmd.new)
Defining command using the basic CmdParse::Command class:
cmd = CmdParse::CommandParser.new( true )
testcmd = CmdParse::Command.new( 'test', true )
testcmd.short_desc = "Short desc"
cmd.add_command( testcmd )
sub = CmdParse::Command.new( 'sub', false )
sub.short_desc = "Add an IP address"
sub.set_execution_block do |args|
puts "Hallo #{args}"
end
testcmd.add_command( sub )
The complete code for this example can be found in the file net.rb of the cmdparse
package!
This tutorial produces a small net application which can add, delete and list IP adresses and show
‘network statistics’. The shown code fragments do not include the whole program. So, instead of
writing all the code yourself, just look at the code fragments first and then use the include
net.rb file for running the program.
Create a new new file and write the necessary require statements.
5 | require 'cmdparse' |
CommandParser classNext we will define our basic CommandParser by defining the name of the program, its version and
the global options. The first boolean argument to the constructor of the CommandParser class
defines whether exceptions should be handled gracefully, i.e. by showing an appropriate error
message and the help screen. The second boolean argument defines whether the top level commands
should use partial command matching instead of full command matching. If partial command matching is
used, then the shortest unambiguous part of a command name can be used instead of always specifing
the full command name.
30 | cmd = CmdParse::CommandParser.new( true, true )
cmd.program_name = "net"
cmd.program_version = [0, 1, 1]
cmd.options = CmdParse::OptionParserWrapper.new do |opt|
opt.separator "Global options:"
opt.on("--verbose", "Be verbose when outputting info") {|t| $verbose = true }
end
|
The options are defined using an option parser wrapper. Currently, the only option parser library
supported is optparse from the Ruby Standard Library. If you want to use another option parser
library, you need to write a wrapper for it so that cmdparse can use it.
Now we only have to tell the program to use our newly defined class to process the command line arguments.
86 | cmd.parse |
The parse method of our CommandParser instance parses the given array of options (or ARGV if
no argument is specified). All the command line options are parsed and the given command executed.
The program can be executed now but won’t be useful as we did not specify any commands.
So, as we have defined our CommandParser object, we need to add some commands to it. First, we
will add two predefined commands, namely the help and the version command.
37 | cmd.add_command( CmdParse::HelpCommand.new ) cmd.add_command( CmdParse::VersionCommand.new ) |
That was easy! Now you can execute the program and specify the commands help or version.
However, we want the program to do something “useful”. Therefore we define a new command.
41 | # ipaddr ipaddr = CmdParse::Command.new( 'ipaddr', true, true ) ipaddr.short_desc = "Manage IP addresses" cmd.add_command( ipaddr ) |
This command is defined by using the default Command class. First an instance is created assigning
a name to the command and defining whether this command takes subcommands and uses partial command
matching. Next we add a short description so that the help command can produce something useful.
And at last, we add this command to our CommandParser instance.
We specified that our ipaddr command takes subcommands. So we have to define them, too:
46 | # ipaddr add
add = CmdParse::Command.new( 'add', false )
add.short_desc = "Add an IP address"
add.set_execution_block do |args|
puts "Adding ip addresses: #{args.join(', ')}" if $verbose
$ipaddrs += args
end
ipaddr.add_command( add )
# ipaddr del
del = CmdParse::Command.new( 'del', false )
del.short_desc = "Delete an IP address"
del.options = CmdParse::OptionParserWrapper.new do |opt|
opt.on( '-a', '--all', 'Delete all IP addresses' ) { $deleteAll = true }
end
del.set_execution_block do |args|
if $deleteAll
$ipaddrs = []
else
puts "Deleting ip addresses: #{args.join(', ')}" if $verbose
args.each {|ip| $ipaddrs.delete( ip ) }
end
end
ipaddr.add_command( del )
# ipaddr list
list = CmdParse::Command.new( 'list', false )
list.short_desc = "Lists all IP addresses"
list.set_execution_block do |args|
puts "Listing ip addresses:" if $verbose
puts $ipaddrs.to_yaml
end
ipaddr.add_command( list, true )
|
We add three subcommands to the ipaddr command: add, del and list.
The add command is similar to the ipaddr command. However, as the add command does not take
other commands, we have to define an execution block.
The del command is similar to the add command. As we want to be able to delete all IP addresses
by issuing only one command, we add an option for this.
By providing true as second argument when we add the list command to the ipaddr command, we
specifiy that this command should be the default command which gets invoked when no command name is
specified on the command line. Only one command can be specified as default command!
Till now we only used the basic Command class to specify commands. However, new commands can also
be created by subclassing the Command class, as shown with this last command:
9 | class NetStatCommand < CmdParse::Command
def initialize
super( 'stat', false )
self.short_desc = "Show network statistics"
self.description = "This command shows very useful network statistics - eye catching!!!"
end
def execute( args )
puts "Showing network statistics" if $verbose
puts
puts "Yeah, I will do something now..."
puts
1.upto(10) do |row|
puts " "*(20-row) + "#"*(row*2 - 1)
end
puts
end
end
|
39 | cmd.add_command( NetStatCommand.new ) |
That’s all! You can run the program now and have a look at the output which differs depending on which arguments you choose.
So, a typical invocation of this program looks like this:
$ ruby net.rb --verbose ipaddr add 192.168.0.1 193.150.0.1
ipaddr is the first command name (which has no options)add is the second command name (which also has no options)$ ruby net.rb
you get an error because you did not specify any command.
However, when you type$ ruby net.rb ipaddr
you do not get an error!
Why? As the ipaddr command takes subcommands there should be an additional command name (e.g.
list) on the command line. However, as the list command is the default command for ipaddr you
do not need to type it.
$ ruby net.rb ip
Why? As partial command matching is used for the top level commands, the shortest unambiguous name
for a command can be used. As there is no other command starting with ip (or even with the letter
i), it is sufficient to write the above to select the ipaddr command.
$ ruby test.rb --verbose mycommand file1 file2 --recursive file3