Разбить строку, содержащую параметры командной строки, в строку [] в Java - PullRequest
26 голосов
/ 15 июля 2010

Подобно этой теме для C # , мне нужно разделить строку, содержащую аргументы командной строки, для моей программы, чтобы я мог позволить пользователям легко запускать несколько команд.Например, у меня может быть следующая строка:

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

Учитывая вышесказанное, Java обычно передает следующее в main:

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

Мне не нужно беспокоиться олюбое расширение оболочки, но оно должно быть достаточно умным, чтобы обрабатывать одинарные и двойные кавычки и любые экранирования, которые могут присутствовать в строке.Кто-нибудь знает способ синтаксического анализа строки, как оболочка при этих условиях?

ПРИМЕЧАНИЕ : мне НЕ необходимо выполнить анализ командной строки, я 'Я уже использую joptsimple , чтобы сделать это.Скорее, я хочу, чтобы моя программа легко создавалась для сценариев.Например, я хочу, чтобы пользователь мог размещать в одном файле набор команд, каждая из которых будет действительна в командной строке.Например, они могут ввести в файл следующее:

--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

Затем пользователь запустит мой инструмент администратора следующим образом:

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

main() найдет --script и перебираем различные строки в файле, разбивая каждую строку на массив, который я затем запускаю в экземпляре joptsimple, который затем передается в мой драйвер приложения.

joptsimple поставляется с Parser, которыйимеет метод разбора , но поддерживает только массив String.Точно так же конструкторы GetOpt также требуют String[] - следовательно, необходим синтаксический анализатор.

Ответы [ 7 ]

24 голосов
/ 16 июля 2010

Вот довольно простая альтернатива для разделения текстовой строки из файла на вектор аргумента, чтобы вы могли передать ее в свой анализатор параметров:

Это решение:

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

Магический класс Commandline является частью ant .Поэтому вы должны либо поместить ant в путь к классам, либо просто взять класс Commandline, поскольку используемый метод является статическим.

9 голосов
/ 15 июля 2010

Вы должны использовать полнофункциональный современный объектно-ориентированный парсер аргументов командной строки. Я предлагаю мой любимый Java простой аргумент парсера как использовать JSAP , это использует Groovy в качестве примера, но то же самое для прямой Java.Существует также args4j , который в некоторых отношениях более современный, чем JSAP, потому что он использует аннотации, держится подальше от материала apache.commons.cli, он старый и перегруженный и очень процедурный и не соответствует Java.его API.Но я все еще прибегаю к JSAP, потому что так легко создавать свои собственные обработчики аргументов.

Существует множество парсеров по умолчанию для URL-адресов, чисел, InetAddress, цвета, даты, файла, класса, и очень легко добавить свой собственный.

Например, вот обработчик длясопоставьте аргументы с Enums:

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

, и я не фанат программирования конфигурации через XML, но у JSAP есть действительно хороший способ объявить опции и настройки вне вашего кода, так что ваш код не засоряетсясотни строк настроек, которые загромождают и затеняют реальный функциональный код, см. мою ссылку на , как использовать JSAP , например, меньше кода, чем в любой другой библиотеке, которую я пробовал.

Это решение вашей проблемы, как разъяснено в вашем обновлении , строки в вашем файле «script» по-прежнему являются командными строками.Прочитайте их из файла построчно и позвоните по номеру JSAP.parse(String);.

. Я использую эту технику, чтобы постоянно обеспечивать функциональность «командной строки» для веб-приложений.Одним из конкретных применений была многопользовательская онлайн-игра с интерфейсом Director / Flash, в которой мы позволяли выполнять «команды» из чата и использовать JSAP на внутреннем интерфейсе для их анализа и выполнения кода на основе того, что он анализировал.Очень похоже на то, что вы хотите сделать, за исключением того, что вы читаете «команды» из файла вместо сокета.Я бы отказался от простой и просто использовать JSAP, вы действительно будете избалованы его мощной расширяемостью.

8 голосов
/ 21 февраля 2015

Если вам нужно поддерживать только UNIX-подобные ОС, есть еще лучшее решение . В отличие от Commandline от ant, ArgumentTokenizer от DrJava более sh -подобен: он поддерживает экранирование!

Серьезно, даже что-то безумное вроде sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""' корректно токенизируется в [bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""] (Кстати, при запуске эта команда выводит "un'kno"wn$$$' with $"$$. "zzz").

3 голосов
/ 27 августа 2016
/**
 * [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()]);
}
1 голос
/ 18 апреля 2017

Расширение Ответ Andreas_D , вместо копирования используйте CommandLineUtils.translateCommandline(String toProcess) из превосходной Plexus Common Utilities библиотеки.

0 голосов
/ 15 июля 2010

Я использую порт Java Getopt , чтобы сделать это.

0 голосов
/ 15 июля 2010

Полагаю, это поможет вам:

CLI

...