Есть ли способ динамически выбрать интерпретатор, который выполняет сценарий? У меня есть скрипт, который я запускаю на двух разных системах, и интерпретатор, который я хочу использовать, находится в разных местах на двух системах. В конечном итоге мне приходится менять строку hashbang каждый раз, когда я переключаюсь. Я хотел бы сделать что-то, что является логическим эквивалентом этого (я понимаю, что эта точная конструкция невозможна):
if running on system A:
#!/path/to/python/on/systemA
elif running on system B:
#!/path/on/systemB
#Rest of script goes here
Или еще лучше, если он попытается использовать первый интерпретатор, а если не найдет, использует второй:
try:
#!/path/to/python/on/systemA
except:
#!path/on/systemB
#Rest of script goes here
Очевидно, что вместо этого я могу выполнить его как
/path/to/python/on/systemA myscript.py
или в
/path/on/systemB myscript.py
зависимости от того, где я нахожусь, но на самом деле у меня есть скрипт-обертка, который запускается myscript.py
, поэтому я хотел бы указать путь к интерпретатору python программно, а не вручную.
if
условия не вариант для вас? как,if something; then /bin/sh restofscript.sh elif...
Ответы:
Нет, это не сработает. Эти два символа обязательно
#!
должны быть первыми двумя символами в файле (как бы вы в любом случае указали, что интерпретирует оператор if?). Это составляет «магическое число», котороеexec()
обнаруживает семейство функций, когда они определяют, является ли файл, который они собираются выполнить, сценарием (который требует интерпретатора) или двоичным файлом (который не выполняет).Формат линии Шебанга довольно строг. У него должен быть абсолютный путь к интерпретатору и не более одного аргумента.
Что вы можете сделать, это использовать
env
:Теперь путь к
env
является обычно/usr/bin/env
, но технически это не гарантия.Это позволяет вам настроить
PATH
переменную среды в каждой системе так, чтобыinterpreter
(будь тоbash
,python
или ,perl
или то , что у вас есть) не найдено.Недостатком этого подхода является то, что невозможно будет передать аргумент интерпретатору.
Это означает, что
и
вряд ли будет работать на некоторых системах.
Другой очевидный подход - использовать автоинструменты GNU (или более простую систему шаблонов), чтобы найти интерпретатор и поместить правильный путь в файл в
./configure
шаг, который будет запускаться после установки скрипта в каждой системе.Можно также прибегнуть к запуску сценария с явным интерпретатором, но это, очевидно, то, чего вы пытаетесь избежать:
источник
#!
должно произойти с самого начала, так как эта строка обрабатывается не оболочкой. Мне было интересно , если есть способ поставить логику внутри в hashbang линии , которая была бы эквивалентна если / другое. Я также надеялся избежать возни с моим,PATH
но я думаю, что это мои единственные варианты.#!/usr/bin/awk
, вы можете предоставить ровно один аргумент, как#!/usr/bin/awk -f
. Если двоичный файл, на который вы указываете, являетсяenv
аргументом двоичного файла, который вы хотитеenv
найти, как в#!/usr/bin/env awk
./usr/bin/env
с одним аргументомawk -f
.foo.awk
с помощью строки hashbang,#!/usr/bin/env awk -f
и вы вызываете его с помощью./foo.awk
затем, то в Linux вы увидитеenv
два параметраawk -f
и./foo.awk
. На самом деле идет поиск/usr/bin/awk -f
(и т. Д.) С пробелом.Вы всегда можете создать скрипт-обертку, чтобы найти правильный интерпретатор для конкретной программы:
Сохраните обертку в пользовательских
PATH
какprogram
и отложите реальную программу в сторону или под другим именем.Я использовал
#!/bin/bash
в hashbang из-заflags
массива. Если вам не нужно хранить переменное количество флагов или что-то подобное, и вы можете обойтись без него, скрипт должен работать переносимо#!/bin/sh
.источник
exec "$interpreter" "${flags[@]}" "$script" "$@"
также видел, чтобы сохранить дерево процессов чище. Также распространяется код выхода.exec
.#!/bin/sh
бы лучше, чем#!/bin/bash
? Даже если/bin/sh
это символическая ссылка на другую оболочку, она должна существовать в большинстве (если не во всех) системах * nix, плюс это заставит автора сценария создавать переносимый сценарий, а не впадать в bashisms.flags
является нестандартной функцией, но он достаточно полезен для хранения переменного количества флагов, поэтому я решил оставить его.script=/what/ever; something && exec this "$script" "$@"; exec that "$script" -x -y "$@"
. Вы также можете добавить проверку ошибок для ошибок exec.Вы также можете написать полиглот (объединить два языка). / bin / sh гарантированно существует.
У этого есть обратная сторона уродливого кода, и, возможно, некоторые
/bin/sh
могут запутаться. Но это может быть использовано, когдаenv
он не существует или существует где-то еще, кроме / usr / bin / env. Его также можно использовать, если вы хотите сделать какой-то довольно необычный выбор.Первая часть скрипта определяет, какой интерпретатор использовать при запуске с / bin / sh в качестве интерпретатора, но игнорируется при запуске правильным интерпретатором. Используйте
exec
для предотвращения запуска оболочки больше, чем первая часть.Пример Python:
источник
exec "$interpreter" "$0" "$@"
передать имя самого скрипта и самому интерпретатору. (А потом надеюсь, никто не соврал при настройке$0
.)#!
, Scala игнорирует все до совпадения!#
; это позволяет вам разместить произвольно сложный код скрипта на произвольном языке, а затемexec
механизм исполнения Scala со скриптом.Я предпочитаю ответы Кусалананды и Илккачу, но вот альтернативный ответ, который более прямо делает то, о чем просил вопрос, просто потому, что он был задан.
Обратите внимание, что вы можете сделать это только тогда, когда интерпретатор разрешает написание кода в первом аргументе. Здесь
-e
и все после него дословно воспринимается как 1 аргумент для ruby. Насколько я могу судить, вы не можете использовать bash для кода shebang, потомуbash -c
что код должен быть в отдельном аргументе.Я попытался сделать то же самое с Python для кода Shebang:
но получается слишком долго и linux (по крайней мере на моей машине) усекает шебанг до 127 символов. Пожалуйста, извините за использование
exec
вставки новых строк, так как python не разрешает try-excepts илиimport
s без новых строк.Я не уверен, насколько это переносимо, и я бы не стал делать это с кодом, предназначенным для распространения. Тем не менее, это выполнимо. Может быть, кто-то найдет это полезным для быстрой и грязной отладки или что-то в этом роде.
источник
Хотя это не выбирает интерпретатор в сценарии оболочки (он выбирает его для каждой машины), это более простая альтернатива, если у вас есть административный доступ ко всем машинам, на которых вы пытаетесь запустить скрипт.
Создайте символическую ссылку (или жесткую ссылку, если необходимо), чтобы указать на нужный путь интерпретатора. Например, в моей системе perl и python находятся в / usr / bin:
создаст символическую ссылку, чтобы разрешить хэш-банг для / bin / perl и т. д. Это также сохраняет возможность передавать параметры в сценарии.
источник
Сегодня я столкнулся с подобной проблемой (
python3
указывающей на версию python, которая была слишком старой в одной системе), и предложил подход, несколько отличающийся от обсуждаемого здесь: используйте «неправильную» версию Python для загрузки в «правильный». Ограничение состоит в том, что некоторая версия python должна быть надежно достижимой, но обычно это может быть достигнуто, например,#!/usr/bin/env python3
.Итак, что я делаю, это начинаю свой скрипт с:
Что это делает:
источник