У меня есть скрипт оболочки, в который я хочу добавить shebang. Дана переменная, определенная следующим образом:
SHEBANG="#!/bin/sh"
Мой вопрос в том, могу ли я использовать эту переменную в другом скрипте, например так:
$SHEBANG
# other stuff...
решение1
Нет. Строка shebang обрабатывается вашим ядром или системной библиотекой* и не обрабатывает переменные оболочки.
Если по какой-то причине вы хотите использовать переменную окружения в качестве интерпретатора, вы можете создать исполняемый файл "trampoline" (не скрипт!), который повторно порождает
$SHEBANG "$@"
. В этом случае вам нужно будет опустить#!
из переменной;#!
полезно только тогда, когда это буквально первые два байта файла.Другой вариант — использоватьтот факт, что скрипты без строк shebang (как правило) будут выполняться как скрипты оболочки с помощью либо
sh
вызывающей оболочки. Если для вашего shebang target есть синтаксис комментариев, который перекрывается с синтаксисом оболочки, вы можете заставить его работать. Эта стратегия является или была обычной для интерпретаторов Lisp. В случае вашего примера, где интерпретатор также являетсяsh
, это нежизнеспособно.Вы можете продвинуться еще дальше в этом направлении, если вы можете рассчитывать на
bash
присутствие: сценарий, первая строка которогоexec /bin/bash -c 'exec "$SHEBANG" <(tail -n +2 "$0")' "$0" "$@"
будет работать для большинства интерпретаторов; Bash используется как промежуточный шаг, так чтозамена процессаможет быть использовано для того, чтобы иметь
tail
пропустите первую строку для нас, чтобы мы не оказались в бесконечном цикле. Некоторые интерпретаторы требуют, чтобы файл был доступный для поиска, и они не примут такой конвейер.
Стоит подумать, действительно ли вы хотите это сделать. Помимо проблем безопасности, это просто не очень полезно: немногие скрипты могут изменять свои интерпретаторы таким образом, и там, где это возможно, вам почти наверняка будет лучше с более ограниченной частью логики, которая порождает правильный для ситуации (возможно, скрипт оболочки между ними, который вызывает интерпретатор с путем к настоящему скрипту в качестве аргумента).
* Не всегда; в определенных обстоятельствах некоторые оболочки считывают его самостоятельно, но они все равно не выполняют расширение переменной.
решение2
Я не знаю, имеете ли вы в виду именно этот вариант использования, но есть одна вещь, которую вы можетеможетсделать это
#!/usr/bin/env python
поэтому ваш скрипт использует первый python
in $PATH
вместо необходимости жесткого кодирования /usr/bin/python
, что может не работать должным образом в некоторых системах, где «хорошая» версия python находится в /usr/local/bin
или /opt
или в чем-то еще.
Почему в качестве моего шебанга лучше использовать "#!/usr/bin/env NAME" вместо "#!/path/to/NAME"?
Это не создает бесконечного цикла, потому что интерпретатор Python (который env
вызывает файл) просто обрабатывает #!
строку как комментарий. Это своего рода смысл дизайна #!
, потому что многие языки используют #
в качестве комментария символ.
Чтобы расширить это,вы могли бы написать программу-полиглоткоторый изначально работает под управлением #!/bin/sh
, но затем выясняет, какой интерпретатор на самом деле использовать, и вызывает его в том же скрипте.
Оказывается, уже естьвеб-страница с многострочными трюками shebangдля многих языков, включая компилируемые языки (скрипт передает часть себя компилятору и запускает вывод).
На странице руководства perlrun
приведен следующий пример в качестве альтернативы #!/usr/bin/env perl
:
#!/bin/sh
#! -*-perl-*-
eval 'exec perl -x -wS $0 ${1+"$@"}'
if 0;
your perl script goes here
При запуске в качестве /bin/sh
скрипта eval
выполняет perl -x -wS
ваш скрипт, передавая ему ваши аргументы.
Когда Perl запускает его, то eval
контролируется на if 0;
следующей строке, поэтому он никогда не запускается. В отличие от sh
, операторы Perl не заканчиваются новой строкой.
Вы можете сделать это более сложным, используя "$SHEBANG"
вместо perl
или даже выполнив несколько sh
команд для выбора используемого интерпретатора Perl.
решение3
Я думаю, это то, что вам нужно:
xb@dnxb:/tmp$ cat /tmp/hole.sh
#!/usr/bin/$shell_var
echo 1
readlink "/proc/$$/exe"
echo 2
function f { echo hello world; }
echo 3
xb@dnxb:/tmp$ chmod +x /tmp/hole.sh
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var
#!/bin/bash
/bin/dash -- "$@"
xb@dnxb:/tmp$ sudo chmod +x /usr/bin/\$shell_var
xb@dnxb:/tmp$ ./hole.sh
1
/bin/dash
2
./hole.sh: 5: ./hole.sh: function: not found
3
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var
#!/bin/bash
/bin/bash -- "$@"
xb@dnxb:/tmp$ ./hole.sh
1
/bin/bash
2
3
xb@dnxb:/tmp$
Объяснение:
Используйте
\$shell_var
файл act как «переменную» для определения нужной вам оболочки.Так как dash не поддерживает
function f { echo hello world; }
синтаксис, поэтому возникла ошибкаfunction: not found
, но изменение оболочки на bash в\$shell_var
файле больше не приведет к возникновению ошибки.Полный путь к файлу (или относительный путь, если ./hole.sh запущен в /tmp) будет передан как
$@
файл\$shell_var
и/tmp/hole.sh
выполнен внутри\$shell_var
.