Java, Runtime.exec или ProcessBuilder: как узнать, является ли файл оболочкой или двоичным?

Я ищу наиболее эффективный способ принять решение:

  • Должен ли я предварять предоставленную пользователем командную строку исполняемым файлом оболочки
  • Если да, то что это будет за исполняемый файл? (/bin/sh? /usr/bin/perl? /usr/bin/ksh? c:/../cmd.exe?)

Известно, что для запуска shell-скрипта из 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)

Если сценарий оболочки выполняется без двоичного файла оболочки, он выдает ошибку:

ProcessBuilder pb = new ProcessBuilder("script.sh", "arg1", "arg2);
(error 2: file not found)

Я нахожусь в ситуации, когда мое приложение не знает, что оно запускает, двоичный файл или скрипт.

Запущенное приложение — это обработчик событий, предоставляемый конечным пользователем. Скорее всего, это сценарий оболочки, выполняемый под Unix; но это может быть *.cmd под Windows или Perl-скрипт, выполняемый под какой-то малоизвестной платформой. В конце концов, это Java.

Моей первой наивной попыткой было запустить командную строку с оболочкой и посмотреть, работает ли она. Если нет, попробуйте выполнить его как двоичный файл.

Это некрасиво и рискованно: под какой-то неизвестной комбинацией платформы и оболочки второй запуск все равно может запустить скрипт во второй раз с непредсказуемыми результатами.

Кроме того, я не могу сказать, когда сценарий запустился нормально и потерпел неудачу из-за какой-то собственной проблемы, когда я просто не могу его запустить.

Лучшее, о чем я сейчас думаю, это:

  • Прочтите сценарий и найдите любые непечатаемые байты
  • Если найдено, считайте его двоичным
  • Если нет, добавьте /bin/sh (или cmd.exe, если под Windows)

Пожалуйста, сообщите, если у вас есть идеи получше.

ОБНОВЛЕНИЕ/ЧАСТИЧНОЕ РЕШЕНИЕ

Спасибо всем, кто поделился со мной своими мыслями.

Оказывается, я запутал себя и весь остальной интернет :)

Не требуется добавлять двоичный файл should перед введенной пользователем командной строкой при условии, что:

  1. скрипт находится в PATH
  2. (для Unix) скрипт является исполняемым
  3. (для Unix) скрипт имеет #!/path/to/interpreter

Пока я тестировал свой код, то одно, то другое из этих условий не выполнялось. :-(

После выполнения теста аккуратно с нуля был выполнен скрипт.

Пункт 3 может быть выполнен только пользователем и должен быть задокументирован в Руководстве пользователя.

Из-за того, как эти сценарии распространяются на целевую систему, они могут быть неисполняемыми и могут отсутствовать в PATH.

Единственный путь, который меня волнует, является относительным, поэтому достаточно добавить ./ к любому относительному пути.

Сделать скрипты исполняемыми под Unix (и любой другой платформой) — более сложная задача. Это не ВОРА. Размещение /bin/sh перед ним может помочь, но, насколько я помню, прямо под Solaris оболочка не будет выполнять неисполняемый сценарий.

Позже на этой неделе я опубликую еще одно обновление.


person Не морозь меня    schedule 09.01.2012    source источник
comment
Есть ли в вашем сценарии оболочки соответствующая строка шебанга (#!) вверху? Если это так, то вы можете просто попросить ядро ​​запустить его (если оно является +x исполняемым), не заботясь о том, какой интерпретатор команд ему может понадобиться.   -  person Greg Hewgill    schedule 09.01.2012
comment
Я не могу знать. Сценарий поставляется конечными пользователями и не поставляется с программным обеспечением.   -  person Не морозь меня    schedule 09.01.2012


Ответы (3)


Это должно быть красным флажком, что вы должны прыгать через эти обручи только для того, чтобы запустить команду. Во-первых, потому что это становится очень сложным, а во-вторых, потому что Java был разработан, чтобы быть независимым от платформы. Когда вы исследуете хаки для конкретных ОС, чтобы заставить работать встроенные классы, вы должны сделать шаг назад и пересмотреть свои предположения.

ProcessBuilder pb = new ProcessBuilder("script.sh", "arg1", "arg2);
(error 2: file not found)

Обратите внимание, что сообщение об ошибке «файл не найден», а не «невозможно выполнить сценарий оболочки» или какая-то подобная ошибка. Наиболее вероятная причина этой ошибки не в том, что вы выполняете скрипт, а в том, что скрипт не может быть найден.

Если скрипт находится в текущем каталоге, вам нужно добавить ./ впереди. Если вы не укажете явный путь к исполняемому файлу, тогда исполняемый файл должен находиться в одном из каталогов в вашей переменной окружения $PATH. Текущий каталог . обычно не включается в $PATH по умолчанию.

ProcessBuilder pb = new ProcessBuilder("./script.sh", "arg1", "arg2);

Если имя сценария является заданным пользователем значением, я бы наложил это требование на пользователя — вы можете добавить для него ./, но программы UNIX обычно стараются не быть слишком полезными. Если они забудут поставить ./, то это их проблемы!

person John Kugelman    schedule 09.01.2012
comment
Спасибо за принудительную промывку моего мозга. Действительно, ./script помогает... по крайней мере частично. - person Не морозь меня; 10.01.2012

Одним из возможных решений является создание скрипта, который обертывает исполняемый скрипт/двоичный файл из вашей программы. Таким образом, вы знаете, что это всегда сценарий. Сгенерированный скрипт просто выполняет внутренний скрипт/двоичный файл и возвращает код ошибки (и, возможно, перенаправляет ввод/вывод). Когда закончите, вы можете просто удалить его. Java позволяет очень легко создавать временные файлы.

person Amir Afghani    schedule 09.01.2012

On

ProcessBuilder pb = new ProcessBuilder("/bin/sh", "/path/binary", "arg1", "arg2); (sh: невозможно выполнить двоичный файл)

ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "/path/binary", "arg1", "arg2); (sh: невозможно выполнить двоичный файл)

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

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

person Jayan    schedule 09.01.2012