Java, Runtime.exec или ProcessBuilder: как узнать, является ли файл оболочкой или двоичным?
Я рассматриваю наиболее эффективный способ решения:
- Должен ли я преподнести предоставленную пользователем командную строку с исполняемым файлом оболочки
- Если да, каков будет этот исполняемый файл? (/bin/sh?/usr/bin/perl?/usr/bin/ksh? c:/../cmd.exe?)
Известно, что для запуска оболочки script из Java следует вместо этого запустить оболочку:
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "script.sh", "arg1", "arg2);
Для запуска двоичного файла необходимо запустить сам файл:
ProcessBuilder pb = new ProcessBuilder("/path/binary", "arg1", "arg2);
Если двоичный файл выполняется с оболочкой, он вызывает ошибку:
ProcessBuilder pb = new ProcessBuilder("/bin/sh", "/path/binary", "arg1", "arg2);
(sh: cannot execute binary file)
Если оболочка script выполняется без двоичного кода оболочки, она вызывает ошибку:
ProcessBuilder pb = new ProcessBuilder("script.sh", "arg1", "arg2);
(error 2: file not found)
Я в ситуации, когда мое приложение не знает, что он запускает, двоичный или script.
Запущенное приложение является обработчиком событий, предоставляемым конечным пользователем. Скорее всего, это оболочка script, выполняемая под Unix; но это может быть *.cmd под Windows или Perl script, выполненный под некоторой неясной платформой. В конце концов, это Java.
Моя первая наивная попытка состояла в том, чтобы запустить командную строку с оболочкой и посмотреть, работает ли она. Если нет, попробуйте выполнить его как двоичный.
Это безобразно и рискованно: при некотором неизвестном сочетании платформы и оболочки второй запуск может по-прежнему выполнять script, второй раз, с непредсказуемыми результатами.
Кроме того, я не могу сказать, когда script запустился ОК и не удалось из-за какой-то собственной проблемы, когда я просто не могу его запустить.
Лучшее, что я сейчас рассматриваю:
- Прочитайте script и найдите любые непечатаемые байты
- Если найдено, считайте его двоичным
- Если нет, добавьте /bin/sh (или cmd.exe, если в Windows)
Пожалуйста, сообщите, если у вас есть какие-то идеи.
ОБНОВЛЕНИЕ/ЧАСТИЧНОЕ РЕШЕНИЕ
Спасибо всем, кто поделился своими мыслями со мной.
Оказывается, я смутил себя и остальную часть Интернета:)
Не требуется добавлять двоичный код перед введенной пользователем командной строкой, если:
- script находится в PATH
- (для Unix) script является исполняемым
- (для Unix) script имеет #!/path/to/interpreter
Пока я тестировал свой код, одно или другое из этих условий не выполнялось.: - (
После тщательного тестирования с нуля script был выполнен.
Точка 3 может выполняться только пользователем и должна быть документирована в Руководстве пользователя.
В связи с тем, как эти сценарии распространяются в целевую систему, они могут быть не исполняемыми и не могут быть в PATH.
Единственный путь, о котором я забочусь, является относительным, поэтому достаточно добавить a./к любому относительному пути.
Создание сценариев, исполняемых в Unix (и любой другой платформе), является более сложной задачей. Это не WORA. Размещение /bin/sh перед ним может помочь, но если я правильно помню Solaris, оболочка не выполнит неисполняемый script.
Я опубликую новое обновление позже на этой неделе.
Ответы
Ответ 1
Это должен быть красный флаг, который вам нужно проскочить через эти обручи, чтобы запустить команду. Во-первых, потому что это становится очень сложным, а во-вторых, потому что Java была разработана как независимая от платформы. Когда вы изучаете взломы, специфичные для ОС, чтобы работать с встроенными классами, вы должны отступить и пересмотреть свои предположения.
ProcessBuilder pb = new ProcessBuilder("script.sh", "arg1", "arg2);
(error 2: file not found)
Обратите внимание, что сообщение об ошибке "файл не найден", а не "невозможно выполнить оболочку script" или некоторую такую ошибку. Наиболее вероятной причиной этой ошибки является не то, что вы выполняете script, но script не может быть найден.
Если script находится в текущем каталоге, вам нужно добавить ./
спереди. Если вы не поместите явный путь к исполняемому файлу, исполняемый файл должен находиться в одном из каталогов в вашей переменной среды $PATH
. Текущий каталог .
обычно не включен в $PATH
по умолчанию.
ProcessBuilder pb = new ProcessBuilder("./script.sh", "arg1", "arg2);
Если имя script является предоставленным пользователем значением, тогда я бы наложил это требование на пользователя - вы могли бы добавить ./
для них, но программы UNIX обычно стараются не быть слишком полезными. Если они забудут положить ./
, то их проблема!
Ответ 2
Одним из возможных решений является создание script, который обертывает исполняемый script/binary из вашей программы. Таким образом, вы всегда знаете это script. Сгенерированный script просто выполняет внутренний script/binary и возвращает код ошибки (и, возможно, перенаправляет ввод/вывод). Когда закончите, вы можете просто удалить его. Java позволяет создавать временные файлы очень легко.
Ответ 3
Вкл
ProcessBuilder pb = new ProcessBuilder ( "/bin/sh", "/path/binary", "arg1", "arg2"; (sh: невозможно выполнить двоичный файл)
ProcessBuilder pb = new ProcessBuilder ( "/bin/sh", "-c", "/path/binary", "arg1", "arg2);
(sh: невозможно выполнить двоичный файл)
Один из вариантов - принять путь интерпретатора как другой аргумент (возможно, из списка известных значений) от пользователей.
(Это мог быть комментарий. Я не мог правильно форматировать)