Разделить строку, содержащую параметры командной строки, в 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[]
- отсюда необходимость в синтаксическом анализаторе.
Ответы
Ответ 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()]);
}
Ответ 5
Развернув Andreas_D answer, вместо используйте CommandLineUtils.translateCommandline(String toProcess)
из отличного Plexus Общие утилиты.
Ответ 6
Я думаю, это поможет вам:
CLI
Ответ 7
Я использую Java Getopt port для этого.