Мне интересно, существует ли общий способ передачи нескольких опций в исполняемый файл через строку shebang ( #!
).
Я использую NixOS, и обычно первая часть шебанга в любом сценарии, который я пишу /usr/bin/env
. Проблема, с которой я сталкиваюсь, состоит в том, что все, что приходит после, интерпретируется системой как один файл или каталог.
Предположим, например, что я хочу написать скрипт, который будет выполняться bash
в режиме posix. Наивный способ написания Шебанга будет:
#!/usr/bin/env bash --posix
но попытка выполнить полученный скрипт выдает следующую ошибку:
/usr/bin/env: ‘bash --posix’: No such file or directory
Я знаю об этом посте , но мне было интересно, было ли более общее и более чистое решение.
РЕДАКТИРОВАТЬ : Я знаю, что для скриптов Guile , есть способ достичь того, что я хочу, документировано в разделе 4.3.4 руководства:
#!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#
Хитрость в том, что вторая строка (начинающаяся с exec
) интерпретируется как код, sh
но, находясь в блоке #!
... !#
, как комментарий, и, следовательно, игнорируется интерпретатором Guile.
Не было бы возможно обобщить этот метод для любого интерпретатора?
Второе РЕДАКТИРОВАНИЕ : после небольшой игры кажется, что для интерпретаторов, которые могут читать их ввод stdin
, будет работать следующий метод:
#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;
Это, вероятно, не оптимально, поскольку sh
процесс длится до тех пор, пока переводчик не завершит свою работу. Любые отзывы или предложения будут оценены.
Ответы:
Не существует общего решения, по крайней мере, если вам нужно поддерживать Linux, потому что ядро Linux рассматривает все, что следует за первым «словом» в строке shebang, как один аргумент .
Я не уверен, каковы ограничения NixOS, но обычно я бы просто написал ваш шебанг как
или, где возможно, установите параметры в скрипте :
В качестве альтернативы, вы можете сделать так, чтобы скрипт сам перезапускался с соответствующим вызовом оболочки:
Этот подход может быть распространен на другие языки, если вы найдете способ игнорировать первую пару строк (которые интерпретируются оболочкой) целевым языком.
GNU
coreutils
'env
обеспечивает обходной путь начиная с версии 8.30, см unode «сек ответ для деталей. (Это доступно в Debian 10 и новее, RHEL 8 и новее, Ubuntu 19.04 и новее и т. Д.)источник
Хотя и не совсем переносимый, начиная с coreutils 8.30 и в соответствии с его документацией вы сможете использовать:
Итак, учитывая:
ты получишь:
и в случае, если вам интересно,
showargs
это:источник
env
где она-S
была добавлена в 2005 году. См. Lists.gnu.org/r/coreutils/2018-04/msg00011.htmlshowargs
: pastebin.com/q9m6xr8H и pastebin.com/gS8AQ5WA (однострочный)env
включает в свойshowargs
: опция -v , например#!/usr/bin/env -vS --option1 --option2 ...
Стандарт POSIX очень лаконичен в описании
#!
:Из обоснования раздела документации
exec()
семейства системных интерфейсов :Из раздела «Введение в оболочку» :
По сути, это означает, что любая реализация (используемая вами Unix) может выполнять специфику разбора строки shebang так, как хочет.
Некоторые Unices, такие как macOS (не могут тестировать ATM), будут разбивать аргументы, переданные интерпретатору в строке shebang, на отдельные аргументы, в то время как Linux и большинство других Unix будут давать аргументы интерпретатору как одну опцию.
Таким образом, неразумно полагаться на то, что линия Шебанга может принимать более одного аргумента.
Смотрите также раздел о переносимости статьи Шебанга в Википедии .
Одним из простых решений, которое можно обобщить для любой утилиты или языка, является создание сценария-оболочки, который выполняет настоящий сценарий с соответствующими аргументами командной строки:
Я не думаю, что я лично попытался бы заставить его перезапустить себя, поскольку это кажется несколько хрупким.
источник
Шебанг описан на
execve
(2) странице руководства следующим образом:В этом синтаксисе допускаются два пробела:
Обратите внимание, что я не использовал множественное число, когда говорил о необязательном аргументе, также как и приведенный выше синтаксис
[optional-arg ...]
, так как вы можете указать не более одного аргумента .Что касается сценариев оболочки, вы можете использовать
set
встроенную команду в начале вашего скрипта, которая позволит устанавливать параметры интерпретатора, обеспечивая тот же результат, что и при использовании аргументов командной строки.В твоем случае:
В командной строке Bash проверьте выходные данные,
help set
чтобы получить все доступные параметры.источник
В Linux шебанг не очень гибкий; согласно множественным ответам (ответ Стивена Китта и Йорга Миттага ), не существует определенного способа передачи нескольких аргументов в строке Шебанга.
Я не уверен, будет ли это кому-нибудь полезно, но я написал короткий скрипт для реализации недостающей функции. См. Https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa .
Также возможно написать встроенные обходные пути. Ниже я представляю четыре не зависящих от языка обходных пути, примененных к одному и тому же тестовому сценарию, и результат, который печатает каждый из них. Я полагаю, что скрипт является исполняемым и находится в
/tmp/shebang
.Оборачиваем ваш скрипт в bash heredoc внутри процесса подстановки
Насколько я знаю, это самый надежный, независимый от языка способ сделать это. Это позволяет передавать аргументы и сохраняет стандартный ввод. Недостатком является то, что интерпретатор не знает (реального) местоположения файла, который он читает.
Печать
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
звонков:Обратите внимание, что подстановка процесса создает специальный файл. Это может не подходить для всех исполняемых файлов. Например,
#!/usr/bin/less
жалуется:/dev/fd/63 is not a regular file (use -f to see it)
Я не знаю, возможно ли иметь heredoc внутри процесса подстановки в тире.
Обернуть ваш скрипт в простой heredoc
Короче и проще, но вы не сможете получить доступ
stdin
из своего скрипта, и для этого требуется, чтобы интерпретатор мог читать и выполнять скрипт изstdin
.Печать
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
звонков:Используйте
system()
вызов awk, но без аргументовПравильно передает имя исполняемого файла, но ваш скрипт не получит аргументы, которые вы ему дадите. Обратите внимание, что awk - единственный из известных мне языков, чей интерпретатор по умолчанию установлен в linux и по умолчанию читает его инструкции из командной строки.
Печать
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
звонков:Используйте
system()
вызов awk 4.1+ , если ваши аргументы не содержат пробеловХорошо, но только если вы уверены, что ваш скрипт не будет вызываться с аргументами, содержащими пробелы. Как вы можете видеть, ваши аргументы, содержащие пробелы, будут разделены, если пробелы не экранированы.
Печать
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
звонков:Для версий awk ниже 4.1 вам придется использовать конкатенацию строк внутри цикла for, см. Пример функции https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .
источник
$variable
или`command`
заменить:exec python3 -O <(cat <<'EOWRAPPER'
Трюк, который нужно использовать
LD_LIBRARY_PATH
с python в#!
строке (shebang), которая не зависит ни от чего, кроме оболочки, и работает на удовольствие:Как объяснено в другом месте на этой странице, некоторые оболочки, такие как,
sh
могут использовать сценарий для своего стандартного ввода.Сценарий мы даем
sh
пытается выполнить команду ,''''
которая упрощена до''
(пустая строка) с помощьюsh
и, конечно , она не сможет выполнить его как нет''
команды, поэтому он обычно выводитline 2: command not found
на стандартный дескриптор ошибки , но мы переадресовать это сообщение , используя2>/dev/null
в самая близкая черная дыра, потому что было бы беспорядочно и запутанноsh
показывать пользователю ее отображение.Затем мы переходим к интересующей нас команде:
exec
которая заменяет текущий процесс оболочки следующим, в нашем случае:/usr/bin/env python
адекватными параметрами:"$0"
дать Python знать, какой скрипт он должен открывать и интерпретировать, а также устанавливатьsys.argv[0]
"$@"
установить Pythonsys.argv[1:]
для аргументов, передаваемых в командной строке скрипта.И мы также просим
env
установитьLD_LIBRARY_PATH
переменную окружения, которая является единственной целью взлома.Команда оболочки заканчивается комментарием, начинающимся с
#
того, что оболочка игнорирует завершающие тройные кавычки'''
.sh
затем заменяется новым экземпляром интерпретатора python, который открывает и читает исходный скрипт python, указанный в качестве первого аргумента (the"$0"
).Python открывает файл и пропускает первую строку исходного кода благодаря
-x
аргументу. Примечание: это также работает без,-x
потому что для Python шебанг это просто комментарий .Затем Python интерпретирует 2-ю строку как строку документации для текущего файла модуля, поэтому, если вам нужна действительная строка документации модуля, просто установите
__doc__
в своей программе Python первое, как в примере выше.источник
''''exec ...
должны выполнить работу. Обратите внимание, что перед exec нет пробела, иначе он будет искать пустую команду. Вы хотите разделить пустое место на первый аргумент, так оно и$0
естьexec
.Я нашел довольно глупый обходной путь, когда искал исполняемый файл, который исключает скрипт в качестве единственного аргумента:
источник