2010-07-15 26 views
23

Similar a this thread for C#, necesito dividir una cadena que contiene los argumentos de línea de comando en mi programa para que pueda permitir a los usuarios ejecutar fácilmente varios comandos. Por ejemplo, podría tener la siguiente cadena:Dividir una cadena que contiene parámetros de línea de comandos en una cadena [] en Java

-p /path -d "here's my description" --verbose other args 

Dado lo anterior, Java pasaría normalmente a lo siguiente en principal:

Array[0] = -p 
Array[1] = /path 
Array[2] = -d 
Array[3] = here's my description 
Array[4] = --verbose 
Array[5] = other 
Array[6] = args 

No necesito que preocuparse acerca de cualquier desarrollo del forro, pero debe ser lo suficientemente inteligente como para manejar comillas simples y dobles y cualquier escape que pueda estar presente dentro de la cadena. ¿Alguien sabe de una forma de analizar la cadena como lo haría el caparazón bajo estas condiciones?

NOTA: hago NO tiene que hacer el análisis de línea de comandos, ya estoy usando joptsimple de hacer eso. Más bien, quiero que mi programa sea fácilmente programable. Por ejemplo, quiero que el usuario pueda colocar dentro de un solo archivo un conjunto de comandos que cada uno de ellos sea válido en la línea de comando. Por ejemplo, puede ser que escriba lo siguiente en un archivo:

--addUser admin --password Admin --roles administrator,editor,reviewer,auditor 
--addUser editor --password Editor --roles editor 
--addUser reviewer --password Reviewer --roles reviewer 
--addUser auditor --password Auditor --roles auditor 

continuación, el usuario podría ejecutar mi herramienta de administración de la siguiente manera:

adminTool --script /path/to/above/file 

main() entonces encontrará la opción --script y iterar sobre las diferentes líneas en el archivo, dividiendo cada línea en una matriz que luego volvería a disparar en una instancia joptsimple que luego pasaría a mi controlador de aplicación.

joptsimple viene con un Analizador que tiene un parse method, pero solo admite una matriz String. Del mismo modo, los constructores GetOpt también requieren un String[] - de ahí la necesidad de un analizador.

+3

¿No podría simplemente usar la matriz args que se le proporcionó en main() en lugar de intentar analizarlo usted mismo? –

+0

He actualizado mi pregunta para describir por qué necesito analizar la cadena y cómo eso es diferente del análisis de línea de comandos. –

+0

No creo que sea diferente al análisis de línea de comandos, consulte el apéndice de mi respuesta sobre cómo me he acercado a algo muy similar a esto en el pasado. –

Respuesta

23

Aquí es una alternativa bastante fácil para dividir una línea de texto de un archivo en un vector de argumento para que pueda darle de comer en su opciones analizador:

Ésta es la solución:

public static void main(String[] args) { 
    String myArgs[] = Commandline.translateCommandline("-a hello -b world -c \"Hello world\""); 
    for (String arg:myArgs) 
     System.out.println(arg); 
} 

El clase mágica Commandline es parte de ant. Entonces, o tiene que poner ant en el classpath o simplemente tomar la clase Commandline ya que el método usado es estático.

+0

Como documentación, los controladores 'translateCommandline' cadenas sueltas y dobles entre comillas y escapes dentro de ellas, pero no reconoce la barra diagonal inversa de la misma manera que un shell POSIX debido a problemas que causan en los sistemas basados ​​en DOS. –

+0

Hay una distribución de fuente de hormiga. En este punto tomaría la implementación de 'translateCommandline' y lo modificaría para adaptarlo a mis necesidades. –

+0

Solución realmente maravillosa. Un millón de gracias. Muy aseado. –

9

Debe utilizar un totalmente equipado orientado a objetos Orientador de argumento de línea de comandos sugiero mi favorito Java Simple Argument Parser. Y how to use JSAP, esto está usando Groovy como ejemplo, pero es lo mismo para Java directo. También hay args4j que es en cierto modo más moderno que JSAP porque usa anotaciones, aléjate de las cosas de apache.commons.cli, es antiguo y rebuscado y muy procesal y no tiene Java-eques en su API. Pero todavía recurro a JSAP porque es muy fácil crear tus propios manejadores de argumentos personalizados.

Hay muchos analizadores predeterminados para URL, números, InetAddress, Color, Fecha, Archivo, Clase, y es muy fácil agregar los suyos propios.

Por ejemplo aquí es un controlador para mapear args a enumeraciones:

import com.martiansoftware.jsap.ParseException; 
import com.martiansoftware.jsap.PropertyStringParser; 

/* 
This is a StringParser implementation that maps a String to an Enum instance using Enum.valueOf() 
*/ 
public class EnumStringParser extends PropertyStringParser 
{ 
    public Object parse(final String s) throws ParseException 
    { 
     try 
     { 
      final Class klass = Class.forName(super.getProperty("klass")); 
      return Enum.valueOf(klass, s.toUpperCase()); 
     } 
     catch (ClassNotFoundException e) 
     { 
      throw new ParseException(super.getProperty("klass") + " could not be found on the classpath"); 
     } 
    } 
} 

y no soy un fan de la programación de configuración a través de XML, pero JSAP tiene una muy buena manera de declarar opciones y configuraciones fuera de su código , por lo que su código no está plagado de cientos de líneas de configuración que desordenan y oscurecen el código funcional real, consulte mi enlace en how to use JSAP para obtener un ejemplo, menos código que cualquiera de las otras bibliotecas que he probado.

Esta es una solución a su problema de dirección como se aclara en su actualización de, las líneas en el archivo de "guión" siguen siendo las líneas de comandos. Léelos desde el archivo línea por línea y llame al JSAP.parse(String);.

Utilizo esta técnica para proporcionar funcionalidad de "línea de comandos" a las aplicaciones web todo el tiempo. Un uso particular fue en un juego en línea multijugador masivo con una interfaz de Director/Flash que permitimos ejecutar "comandos" desde el chat y usar JSAP en el back-end para analizarlos y ejecutar código en función de lo que analizó. Muy parecido a lo que quieres hacer, excepto que lees los "comandos" de un archivo en lugar de un socket. Me gustaría dejar de lado joptsimple y simplemente usar JSAP, realmente se sentirá mimado por su poderosa extensibilidad.

+0

JSAP es el primer analizador que he visto para aceptar una cadena pero, desafortunadamente, devuelve un 'JSAPResult' en lugar de 'String []', así que no podré usarlo sin cambiar mi biblioteca de análisis de línea de comandos :(. –

+0

a 'String [] 'es bastante inútil, la razón completa del resultado de JSAP es que lo hace todo el análisis y la aplicación de reglas y la comprobación de usted. Creo que si realmente retrocede desde donde está replanteando su enfoque y alguna refactorización será realmente beneficiosa. Consulte mi actualización en función de su última edición. –

+0

No quiero construir un analizador de cadenas de shell. 'line.split (" ")' no es lo suficientemente inteligente.Moriría en el parámetro que crea 'Array [3]' como indiqué en mi publicación ya que los parámetros pueden tener tanto espacios como secuencias de escape dentro de ellos. Necesito un analizador completo para manejar todas las posibilidades, pero necesito una cadena para el analizador String [], en lugar de un analizador de línea de comando. –

-1

supongo que éste le ayudará a:

CLI

+0

esto es viejo y reventado y una sugerencia terrible, hay al menos 3 nuevos compatibles y no Las alternativas con todas las funciones con fallas para esto, no uses esto. –

+3

@fuzzy Es bueno saberlo. Pero la votación negativa no era necesaria ya que esta respuesta es correcta. ;-) – InsertNickHere

+0

un mal consejo es peor que ningún consejo :-( –

-1

uso el Java Getopt port para hacerlo.

+1

A menos que me haya perdido algo, el puerto getopt no tomar una cadena, solo un 'String []'. –

+0

¿Podría explicar cómo usarlo? Solo un enlace no es tan bueno. –

8

Si necesita admitir solo sistemas operativos tipo UNIX, existe una solución aún mejor. A diferencia de Commandline de hormiga, ArgumentTokenizer de DrJava es más sh -like: admite escapes!

serio, incluso algo loco como se sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""' adecuadamente tokenized en [bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""] (Por cierto, cuando se ejecuta, salidas de este comando "un'kno"wn$$$' with $"$$. "zzz").

1
/** 
* [code borrowed from ant.jar] 
* Crack a command line. 
* @param toProcess the command line to process. 
* @return the command line broken into strings. 
* An empty or null toProcess parameter results in a zero sized array. 
*/ 
public static String[] translateCommandline(String toProcess) { 
    if (toProcess == null || toProcess.length() == 0) { 
     //no command? no string 
     return new String[0]; 
    } 
    // parse with a simple finite state machine 

    final int normal = 0; 
    final int inQuote = 1; 
    final int inDoubleQuote = 2; 
    int state = normal; 
    final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true); 
    final ArrayList<String> result = new ArrayList<String>(); 
    final StringBuilder current = new StringBuilder(); 
    boolean lastTokenHasBeenQuoted = false; 

    while (tok.hasMoreTokens()) { 
     String nextTok = tok.nextToken(); 
     switch (state) { 
     case inQuote: 
      if ("\'".equals(nextTok)) { 
       lastTokenHasBeenQuoted = true; 
       state = normal; 
      } else { 
       current.append(nextTok); 
      } 
      break; 
     case inDoubleQuote: 
      if ("\"".equals(nextTok)) { 
       lastTokenHasBeenQuoted = true; 
       state = normal; 
      } else { 
       current.append(nextTok); 
      } 
      break; 
     default: 
      if ("\'".equals(nextTok)) { 
       state = inQuote; 
      } else if ("\"".equals(nextTok)) { 
       state = inDoubleQuote; 
      } else if (" ".equals(nextTok)) { 
       if (lastTokenHasBeenQuoted || current.length() != 0) { 
        result.add(current.toString()); 
        current.setLength(0); 
       } 
      } else { 
       current.append(nextTok); 
      } 
      lastTokenHasBeenQuoted = false; 
      break; 
     } 
    } 
    if (lastTokenHasBeenQuoted || current.length() != 0) { 
     result.add(current.toString()); 
    } 
    if (state == inQuote || state == inDoubleQuote) { 
     throw new RuntimeException("unbalanced quotes in " + toProcess); 
    } 
    return result.toArray(new String[result.size()]); 
} 
Cuestiones relacionadas