Основная проблема в том, что java вообще ничего не делает, чтобы разобрать строку, которую вы ей дали, и аккуратно разбить ее на аргументы.
Краткий ответ - использовать String []
версию Runtime.exec
:
Runtime.getRuntime().exec(
new String[] {"/bin/bash", "myscript", "arg1", "arg2", "", "arg4"});
Если у вас есть другие места, где разбор аргументов более запутан, чем этот, вы можете передать разбор строки в bash и выполнить:
Runtime.getRuntime().exec(
new String[] {"/bin/bash", "-c", "/bin/bash myscript arg1 arg2 '' arg4"});
Если вы конвертируете что-то, что выполняет огромное количество сложных перенаправлений, таких как 2>&1
или настраивает весь конвейер, вам может понадобиться этот bash -c
трюк.
EDIT:
Чтобы понять, что здесь происходит, вы должны понимать, что когда код пользовательского пространства говорит ядру «загрузить этот исполняемый файл с этими аргументами и запустить процесс, основанный на этом» (*), то, что передается ядру, является исполняемый файл, массив строк для аргументов (0-й / первый элемент этого массива аргументов - это имя исполняемого файла, за исключением случаев, когда выполняется что-то странное), а также массив строк для среды.
Что делает bash, когда видит эту строку:
/bin/bash myscript arg1 arg2 '' arg4
означает «Хорошо, /bin/bash
не является встроенной функцией, поэтому я выполняю что-то. Давайте соберем массив аргументов для подпроцесса, используя мои алгоритмы синтаксического анализа строк, которые знают о кавычках и еще много чего». Затем Bash определяет, что аргументы для передачи ядра новому процессу:
/bin/bash
myscript
arg1
arg2
(пустая строка)
arg4
Теперь bash имеет довольно сложные алгоритмы обработки строк, которые он применяет здесь - он принимает два разных типа кавычек, он будет делать расширение $VAR
, когда это происходит вне строк или внутри двойных кавычек, он заменяет подкоманды в обратных кавычках с выходом и т. д.
Java не делает ничего сложного, когда вы вызываете одностроковую версию exec
. Он просто создает новый StringTokenizer
и использует его для разбиения строки, которую вы даете, в аргументы. Этот класс ничего не знает о кавычках; это разбивает эту строку на:
Затем Java вызывает String[]
версию exec
. (Ну, один из них)
Заметки для людей, выбирающих гниды:
(*) Да, я намеренно исключаю разницу между системными вызовами fork
и execve
. Притворись, что они один звонок на данный момент.