Подтвердить что ты не робот

Разделить строку, содержащую параметры командной строки, в String [] в Java

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

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

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

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

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

ПРИМЕЧАНИЕ: мне нужно НЕ выполнить синтаксический анализ командной строки, я уже использую 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 поставляется с парсером, который имеет метод parse, но он поддерживает только массив String. Аналогично, конструкторы GetOpt также требуют String[] - отсюда необходимость в синтаксическом анализаторе.

4b9b3361

Ответ 1

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

Это решение:

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, поскольку используемый метод является статическим.

Ответ 2

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

Существует множество парсеров по умолчанию для URL-адресов, чисел, InetAddress, Color, Date, File, Class, и очень просто добавить свои собственные.

Например, это обработчик для сопоставления args с 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 на задней панели, чтобы проанализировать их и выполнить код на основе того, что он анализировал. Очень похоже на то, что вы хотите сделать, за исключением того, что вы читаете "команды" из файла вместо сокета. Я бы отскочил от joptsimple и просто использовал JSAP, вы действительно будете избалованы своей мощной расширяемостью.

Ответ 3

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

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

Ответ 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()]);
}

Ответ 6

Я думаю, это поможет вам:

CLI