Я ищу библиотеку (Java/Scala), которая с учетом команды и списка аргументов создает String, которая при вставке в терминал bash выполнит данную команду со всеми ее аргументами (правильно экранированными).
Мне известен класс java.lang.ProcessBuilder. Это хорошо работает, если я хочу выполнить команду как подпроцесс. Вместо этого я ищу библиотеку, которая может генерировать строковое представление команды.




Обычно я бы не советовал людям пытаться писать свои собственные эскейперы, но в этом случае это не страшно, потому что строки в одинарных кавычках bash будут правильно заключать каждый отдельный символ, кроме байтов NUL и вложенных одинарных кавычек:
public static String bashEscape(String[] arguments) {
StringBuilder sb = new StringBuilder();
for(String arg : arguments) {
if (arg.indexOf(0) != -1) {
throw new IllegalArgumentException("Shell arguments can't represent NUL bytes");
}
sb.append("'").append(arg.replaceAll("'", "'\\\\''")).append("' ");
}
return sb.toString();
}
Это правильно убегает:
*?![]()|<>&;$ и пробелы.Единственное, о чем нужно помнить, это то, что вы и ваша оболочка должны согласовать кодировку символов, когда вы передаете ей строку.
Вы можете распечатать команду, которую собирается запустить ProcessBuilder, а затем не запускать ее. «cmd» будет любой командой, которую ваше приложение генерирует на основе аргументов.
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
String command = processBuilder.command().toString();
Я попробовал это с cmd = "ls", что заставило command содержать [ls]. Когда я копирую это в свою оболочку, я получаю bash: [ls]: command not found. Если я ввожу ls напрямую, файлы отображаются как обычно. У вас другой результат?
Ах да, processBuilder.command() возвращает List<String>, так что вам потребуется либо String.join("", processBuilder.command()), который даст вам команду, либо итерацию по списку, добавляя каждый элемент в String, и это сработает. Я предполагаю, что у вас действительно будет только один элемент в списке, поэтому String.join("", processBuilder.command()) будет легко и просто.
Обычно у вас есть несколько элементов, например. new ProcessBuilder("ls", "-l", "My File.txt"); Если вы присоединитесь к "", вы получите ls-lMy File.txt, что приведет к ls-lMy: command not found. Если вы присоединитесь к пробелам, вы получите ls -l My File.txt, что приведет к ls: cannot access 'My': No such file or directory. Эквивалентная команда bash в этом случае должна быть, например. ls -l "My File.txt" чтобы сохранить пробел в последнем аргументе
Возможно, вам было бы полезнее опубликовать свой код, чтобы я мог точно ответить на примере, который соответствует вашим потребностям. Я понимаю, что processbuilder принимает несколько параметров. Вы действительно заполняете его несколькими параметрами? У вас есть пример того, как вы это делаете? Если их можно соединить пробелами, то разве это не решит проблему? Ваша команда будет выполнена успешно, если вы присоединяетесь с помощью пробелов, и приведенный выше пример ProcessBuilder действительно является тем, что вы используете в своем коде.
Я не ОП, но нет, в моем примере объединение пробелов не работает, потому что это не учитывает пробелы в одном аргументе. Вы можете быть уверены, что OP будет использовать несколько параметров, потому что они спрашивают о «команде и списке аргументов», а ProcessBuilder не поддерживает аргументы без нескольких параметров (например, вы не можете использовать new ProcessBuilder("ls -l 'My File.txt'"), потому что ProcessBuilder не вызывает оболочку).
Небольшим улучшением было бы использование
String#replaceвместоString#replaceAll, так как в этом случае нам не нужно сопоставление регулярных выражений.