Dividir una cadena que contiene parámetros de línea de comandos en un String[] en Java

Similar a este hilo para C#, necesito dividir una cadena que contiene los argumentos de línea de comandos para mi programa, de modo que puede permitir a los usuarios ejecutar varios comandos. Por ejemplo, yo podría tener la siguiente cadena:

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

Dado lo anterior, Java normalmente pase el siguiente a 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

Yo no necesita preocuparse acerca de cualquier expansión de la shell, pero debe ser lo suficientemente inteligente como para manejar las comillas dobles y simples y cualquier escapa que pueden estar presentes dentro de la cadena. ¿Alguien sabe de una forma de analizar la cadena como la shell en estas condiciones?

NOTA: yo NO necesidad de hacer de la línea de comandos de análisis, ya estoy utilizando joptsimple a hacer eso. Más bien, quiero hacer que mi programa fácilmente secuencias de comandos. Por ejemplo, yo quiero que el usuario pueda colocar dentro de un solo archivo de un conjunto de comandos que cada uno de los cuales sería válida en la línea de comandos. Por ejemplo, se podría escribir 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

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

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

main() continuación se encuentra la --script opción y iterar a través de las diferentes líneas en el archivo, la división de cada línea en una matriz que yo quisiera, a continuación, devolver el fuego en un joptsimple instancia que luego se pasó a mi solicitud conductor.

joptsimple viene con un Analizador que tiene un método parse, pero sólo es compatible con un String matriz. Del mismo modo, el GetOpt constructores también requieren de una String[] — de ahí la necesidad de un analizador.

  • No podía usted sólo tiene que utilizar la matriz args dado en main (), en lugar de tratar de analizar a ti mismo?
  • He actualizado mi pregunta para describir por qué es necesario analizar la cadena y la forma en que diferentes desde la línea de comandos de análisis.
  • Creo que no es diferente de la línea de comandos de análisis, consulte el apéndice a mi respuesta sobre cómo me he acercado algo muy similar a esto en el pasado.
  • acaba de agregar una pregunta de respuesta corta que puede encontrar útiles – ahora que ha añadido algunos explanaitions a su pregunta 🙂

6 Kommentare

  1. 26

    Aquí es bastante fácil alternativa para la división de una línea de texto desde un archivo en un vector de argumento, de modo que se puede alimentar a sus opciones de analizador:

    Esta 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);
    }

    La magia de la clase línea de Comandos es parte de hormiga. Así que usted tiene que poner hormiga en el classpath o simplemente tomar la línea de Comandos de clase como el método utilizado es estática.

    • Como parte de la documentación, translateCommandline maneja tanto entre comillas simples y dobles cuerdas y se escapa dentro de ellos, pero no reconoce que haya en la misma manera como POSIX debido a los problemas que causa en los sistemas basados en DOS.
    • Hay una fuente de distribución de hormiga. En este punto me gustaría tener la aplicación de translateCommandline y modificarlo para que se ajuste a mis necesidades.
    • Cuidado, \t\r\n no son espacios en blanco para este método
    • Siendo la única manera ? Nada en el núcleo de las bibliotecas ?
    • Implementación (línea 337): translateCommandline
  2. 9

    Si usted necesita para apoyar sólo UNIX-como sistemas operativos, existe una mejor solución. A diferencia de Commandline de hormiga, ArgumentTokenizer de DrJava es más shcomo: apoya escapa!

    Serio, incluso algo loco como sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""' obtiene correctamente señalizado en [bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""] (Por cierto, cuando se ejecuta, este comando genera "un'kno"wn$$$' with $"$$. "zzz").

  3. 8

    Debe utilizar un completo moderno orientado a objetos Argumento de Línea de Comandos Analizador sugiero mi favorito Java Simple Argumento De Analizador. Y cómo utilizar JSAP, esto es usando Groovy como un ejemplo, pero es el mismo para la recta de Java. También hay args4j que es en cierto modo más moderno de JSAP porque utiliza anotaciones, manténgase alejado de los apache.commons.cli cosas, es viejo y roto y muy procesal y de la onu-Java-eques en su API. Pero todavía me caen de nuevo en JSAP, porque es tan fácil de construir tu propio argumento de los controladores.

    Hay un montón de incumplimiento de los Analizadores para direcciones Url, Números, InetAddress, Color, Fecha, Archivo, Clase, y es super fácil de añadir su propio.

    Por ejemplo, aquí es un controlador de mapa args a las 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 yo no soy un fan de la configuración de la programación a través de XML, pero JSAP tiene una forma muy agradable para declarar opciones y valores de configuración fuera de su código, de modo que el código no está llena de cientos de líneas de configuración que satura y oculta el verdadero código funcional, ver mi enlace en cómo utilizar JSAP por ejemplo, menos código que cualquiera de las otras bibliotecas que he probado.

    Esta es una dirección de la solución a su problema, como queda claro en su actualización, las líneas de su «script» archivo son todavía de líneas de comando. Leer desde el archivo línea por línea y de llamada JSAP.parse(String);.

    Yo uso esta técnica para proporcionar «línea de comandos» de la funcionalidad a las aplicaciones web todo el tiempo. Un uso particular fue en un Juego Multijugador Masivo Online con un Director/Flash frente que hemos habilitado la ejecución de «comandos» de la charla gusta y utiliza JSAP en la parte de atrás para analizar y ejecutar un código basado en lo que se analiza. Muy parecido a lo que están queriendo hacer, excepto de leer los «comandos» de un archivo en lugar de un socket. Me gustaría zanja joptsimple y sólo tiene que utilizar JSAP, se echan a perder por su potente extensibilidad.

    • JSAP es el primer analizador he visto a aceptar una cadena pero, por desgracia, devuelve un JSAPResult en lugar de un String[], así que no voy a ser capaz de utilizarlo sin tener que cambiar de mi línea de comandos biblioteca de análisis :(.
    • un String[] es bastante inútil, toda la razón para JSAP resultado es que lo hace todo el análisis y el cumplimiento de las reglas y la comprobación para usted. Yo creo que si realmente dar un paso atrás de donde son un replanteamiento de su enfoque y algunos de refactorización será realmente beneficioso. Ver mi actualización basado en su última edición.
    • No quiero crear un shell analizador de cadena. line.split(" ") no es casi lo suficientemente inteligente. Iba a morir en el parámetro que crea Array[3] como he indicado en mi post como parámetros pueden tener ambos espacios y secuencias de escape dentro de ellos. Necesito un analizador completo para manejar todas las posibilidades, pero necesito una cadena String[] analizador, en lugar de un analizador de línea de comandos.
    • JSAP podría tomar un par de va a la lectura a través de la documentación para entender las opciones que suministra, pero es una muy buena solución para la línea de comandos de análisis de requisitos y funciona bien sin duda recomendable…
    • tal vez el cambio es la mejor cosa que usted puede hacer joptsimple es, probablemente, demasiado «simple» para sus requisitos.
    • Un String[] es muy útil para mí, como ya tengo un joptsimple analizador de línea de comandos que se encarga de todos los comandos que se emitirá dentro de mi archivo. Yo sólo necesitan para convertir la cadena en un String[] con el fin de ser capaz de proporcionar fácil de secuencias de comandos de varios comandos.
    • JSAP del CommandLineTokenizer está muy cerca de lo que yo necesito (y puede ser suficiente). Se analiza la cadena, como Windows 2000, en lugar de como un shell de Unix.
    • La conmutación de un poco de código para JSAP será mucho menos trabajo que la escritura de un ANTLR gramática para analizar un UNIX estilo de línea de comandos 🙂

  4. 4
    /**
     * [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()]);
    }
  5. -1

    Yo uso el Java Getopt puerto para hacerlo.

    • A menos que me haya perdido de algo, la getopt puerto no toma en una cadena, sólo un String[].
    • podría usted comentar sobre cómo la uso? Sólo un enlace no es tan bueno.

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea