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: невозможно выполнить двоичный файл)

Один из вариантов - принять путь интерпретатора как другой аргумент (возможно, из списка известных значений) от пользователей.

(Это мог быть комментарий. Я не мог правильно форматировать)