Procesado de la Línea de Comandos

La clase CliBuilder

La clase CliBuilder proporciona un conjunto de métodos para el procesado de la línea de comandos.

Most of the time we pass arguments to the script we invoke. These arguments are available as a String[] array in our script. For example to get the first argument we can use the following code s = args[0]. To have real argument handling in our script we use Groovy's CliBuilder class. This class uses Jakarta Commons CLI under the hood. With CliBuilder we can define the argument options and parse the arguments.

Ejemplo Simple

El siguiente ejemplo muestra la forma de uso:

generaciondecodigos@nereida:~/Lgroovy/files$ cat -n cli.groovy 
   1  #!/usr/bin/env groovy
   2  //http://groovy.codehaus.org/gapi/groovy/util/CliBuilder.html
   3  import groovy.util.CliBuilder
   4  
   5  def cli = new CliBuilder(usage:'cli.groovy')
   6  
   7  cli.a(longOpt:'all',  'display all files')
   8  cli.l(longOpt:'long', 'use a long listing format')
   9  cli.t(longOpt:'time', 'sort by modification time')
  10  cli.t(longOpt:'time', 'sort by modification time')
  11  cli.h(longOpt: 'help', 'Show usage information')
  12  
  13  def options = cli.parse(args)
  14  
  15  println options
  16  
  17  println options.arguments() 
  18  
  19  println "a = "+options.a 
  20  println "l = "+options.l 
  21  println "long = "+options.long 
  22  println "t = "+options.t
  23  println "h = "+options.h
  24  
  25  if (options.h) cli.usage()
Cuando lo ejecutamos obtenemos estas salidas:
generaciondecodigos@nereida:~/Lgroovy/files$ ./cli.groovy -h
groovy.util.OptionAccessor@396cbd97
[]
a = false
l = false
long = false
t = false
h = true
usage: cli.groovy
 -a,--all    display all files
 -h,--help   Show usage information
 -l,--long   use a long listing format
 -t,--time   sort by modification time
generaciondecodigos@nereida:~/Lgroovy/files$ ./cli.groovy -l s*.groovy
groovy.util.OptionAccessor@121321f5
[slurpFile2.groovy, slurpFile.groovy, socket.groovy, stdin2.groovy, stdin.groovy]
a = false
l = true
long = true
t = false
h = false
generaciondecodigos@nereida:~/Lgroovy/files$ ./cli.groovy --long s*.groovy
groovy.util.OptionAccessor@121321f5
[slurpFile2.groovy, slurpFile.groovy, socket.groovy, stdin2.groovy, stdin.groovy]
a = false
l = true
long = true
t = false
h = false
generaciondecodigos@nereida:~/Lgroovy/files$ ./cli.groovy -lat s*.groovy
groovy.util.OptionAccessor@693e4a5a
[slurpFile2.groovy, slurpFile.groovy, socket.groovy, stdin2.groovy, stdin.groovy]
a = true
l = true
long = true
t = true
h = false

Un Ejemplo más Complejo

El siguiente programa copia un fichero en otro filtrando las líneas según un comando especificado en línea de argumentos. Sigue un ejemplo de ejecución:

~/Lgroovy/files$ cat sample.txt
1
2
3
4
~/Lgroovy/files$ ./cf -vo t.txt -f 'line.replaceFirst(/1/, "One")' sample.txt
Using filter 'line.replaceFirst(/1/, "One")' on file 'sample.txt' to produce output in 't.txt'
~/Lgroovy/files$ cat t.txt 
One
2
3
4

El programa admite opciones como -v (verbose), -h (ayuda), -o (salida) y -f (filtro).

~/Lgroovy/files$ cat -n cf
 1  #!/usr/bin/env groovy
 2  //http://groovy.codehaus.org/gapi/groovy/util/CliBuilder.html
 3  import groovy.util.CliBuilder
 4  
 5  def cli = new CliBuilder(usage: 'cf -[hof] source')
 6  
 7  // Create the list of options.
 8  cli.with {
 9      h longOpt: 'help', 'Show usage information'
10      v longOpt: 'verbose', 'verbose output'
11      o longOpt: 'output', args: 1, argName: 'output', 'Output file'
12      f longOpt: 'filter', args: 1, argName: 'filter', 'Groovy Code for line'
13  }

El Método with

Groovy has a with method we can use to group method calls and property access to an object. The with method accepts a closure and every method call or property access in the closure applies to the object if applicable.
El siguiente ejemplo muestra el uso del método with:
~/Lgroovy/learning$ cat -n with2.groovy 
 1  #!/usr/bin/env groovy
 2  class Sample {
 3      String username
 4      String email
 5      List<String> labels = []
 6      def speakUp() { "I am $username" }
 7      def addLabel(value) { labels << value }
 8  }
 9  
10  def sample = new Sample()
11  sample.with {
12      username = 'fulanito'
13      email = 'email@host.com'
14      println speakUp()  
15      addLabel 'Groovy' 
16      addLabel 'Java'    
17      addLabel 'Perl'    
18  }
19  
20  sample.with {
21      println labels.size()
22      println labels[0]
23      println labels[1]
24      println username
25      println email
26  }
27
Sigue una ejecución:
~/Lgroovy/learning$ ./with2.groovy 
I am fulanito
3
Groovy
Java
fulanito
email@host.com

Análisis de los Argumentos en Línea de Comandos: parse

El método parse de La clase CliBuilder:

OptionAccessor parse(def args)
nos retorna un objeto OptionAccessor que nos permite acceder a las opciones especificadas en línea de comandos. Retorna un null cuando la línea de comandos esta mal formada.

Véase un ejemplo de uso en la groovyShell

groovy:000> cli = new CliBuilder()
===> groovy.util.CliBuilder@da3b359
groovy:000> cli.v(longOpt: 'verbose', 'verbose mode')
===> null
groovy:000> cli.D(longOpt: 'Debug', 'display debug info')
===> null
groovy:000> cli.o(longOpt: 'output', args:1, 'use/specify output file')
===> null
groovy:000> options = cli.parse(['-v', '-D', '-o mifichero'])
===> groovy.util.OptionAccessor@1952853d
groovy:000> options.v
===> true
groovy:000> options.D
===> true
groovy:000> options.o
===>  mifichero
groovy:000> options.output
===>  mifichero
groovy:000> options.verbose
===> true

Los métodos v, D y o del objeto cli son creados dinámicamente. Del mismo modo, el objeto options tiene atributos v, D y o que han sido creados dinámicamente. Obsérvese que ademas existen alias con los nombres largos para dichos atributos.

Comprobando la Definición de cada Argumento

Asi pues, nuestro programa continua con el análisis de la línea de comandos. Si la opción es -h se muestra la ayuda:

15  def options = cli.parse(args)
16  
17  if (options.h) {
18      cli.usage()
19      println '''
20  Examples:
21     $ ./cf -o t.txt -f 'line.replaceFirst(/1/, "One")' sample.txt
22     $ ./cf -vo t.txt -f 'line.replaceFirst(/1/, "One")' sample.txt
23  '''
24      return 0
25  }
Si no se especifica un filtro se le da un valor por defecto:
27  def filter = (options.f)? options.f : 'println line'
A continuación se analizan los restantes argumentos: los argumentos que no están asociados con una opción. En nuestro caso se trata del nombre del fichero fuente:
29  def extraArguments = options.arguments()
30  if (extraArguments) {
31     source = new File(extraArguments[0])
32  }
33  else {
34     println "Don't forget to specify the source file"
35     return -3
36  }
El nombre del fichero de salida sigue a la opción -o:
38  def target = options.o ? new File(options.o) : new File('salida.tmp')
39  
40  if (options.v) println "Using filter '$filter' on file '$source' to produce output in '$target'"

Copia de un Fichero en Otro con Filtrado Previo

Nuestro programa copia cada línea del fichero fuente source en el fichero target aplicándole previamente el código especificado en filter. Para ello usamos la clase GroovyShell. Primero compilamos el texto usando shell.parse y después ejecutamos el script resultante usando script.run:

42  def shell = new GroovyShell(binding)
43  try {
44    script = shell.parse(filter)
45  }
46  catch(e) {
47    println "ERROR compiling '$filter'"
48    System.exit(-2)
49  }
50  
51  target.withWriter { file ->
52      source.eachLine {
53          line = it
54          try {
55            result = script.run()
56          }
57          catch (e) {
58            println "ERROR evaluating '$filter'"
59            System.exit(-1)
60          }
61          result == null ? file.writeLine(line) : file.writeLine(result as String)
62      }
63  }

Código Completo

~/Lgroovy/files$ cat -n cf
 1  #!/usr/bin/env groovy
 2  //http://groovy.codehaus.org/gapi/groovy/util/CliBuilder.html
 3  import groovy.util.CliBuilder
 4  
 5  def cli = new CliBuilder(usage: 'cf -[hof] source')
 6  
 7  // Create the list of options.
 8  cli.with {
 9      h longOpt: 'help', 'Show usage information'
10      v longOpt: 'verbose', 'verbose output'
11      o longOpt: 'output', args: 1, argName: 'output', 'Output file'
12      f longOpt: 'filter', args: 1, argName: 'filter', 'Groovy Code for line'
13  }
14  
15  def options = cli.parse(args)
16  
17  if (options.h) {
18      cli.usage()
19      println '''
20  Examples:
21     $ ./cf -o t.txt -f 'line.replaceFirst(/1/, "One")' sample.txt
22     $ ./cf -vo t.txt -f 'line.replaceFirst(/1/, "One")' sample.txt
23  '''
24      return 0
25  }
26  
27  def filter = (options.f)? options.f : 'println line'
28  
29  def extraArguments = options.arguments()
30  if (extraArguments) {
31     source = new File(extraArguments[0])
32  }
33  else {
34     println "Don't forget to specify the source file"
35     return -3
36  }    
37  
38  def target = options.o ? new File(options.o) : new File('salida.tmp')
39  
40  if (options.v) println "Using filter '$filter' on file '$source' to produce output in '$target'"
41  
42  def shell = new GroovyShell(binding)
43  try {
44    script = shell.parse(filter)
45  }
46  catch(e) {
47    println "ERROR compiling '$filter'"
48    System.exit(-2)
49  }
50  
51  target.withWriter { file ->
52      source.eachLine {
53          line = it
54          try {
55            result = script.run()
56          }
57          catch (e) {
58            println "ERROR evaluating '$filter'"
59            System.exit(-1)
60          }
61          result == null ? file.writeLine(line) : file.writeLine(result as String)
62      }
63  }



Subsecciones
Casiano Rodríguez León
2010-04-30