Могу ли я использовать переменное содержимое в качестве притона?

Могу ли я использовать переменное содержимое в качестве притона?

У меня есть скрипт оболочки, в который я хочу добавить 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

поэтому ваш скрипт использует первый pythonin $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$ 

Объяснение:

  1. Используйте \$shell_varфайл act как «переменную» для определения нужной вам оболочки.

  2. readlink "/proc/$$/exe"распечатает текущую оболочку.

  3. Так как dash не поддерживаетfunction f { echo hello world; }синтаксис, поэтому возникла ошибка function: not found, но изменение оболочки на bash в \$shell_varфайле больше не приведет к возникновению ошибки.

  4. Полный путь к файлу (или относительный путь, если ./hole.sh запущен в /tmp) будет передан как $@файл \$shell_varи /tmp/hole.shвыполнен внутри \$shell_var.

Связанный контент