Я заметил, что некоторые сценарии, которые я приобрел у других, имеют шебанг, в #!/path/to/NAME
то время как другие (использующие тот же инструмент, NAME) имеют шебанг #!/usr/bin/env NAME
.
Кажется, что оба работают правильно. В руководствах (например, по Python), кажется, есть предположение, что последний шебанг лучше. Но я не совсем понимаю, почему это так.
Я понимаю, что для использования последнего шебанга ИМЯ должна быть в ПУТИ, тогда как у первого шебанга нет этого ограничения.
Кроме того, мне кажется, что первым будет лучший шебанг, поскольку он точно указывает, где находится NAME. Таким образом, в этом случае, если существует несколько версий NAME (например, / usr / bin / NAME, / usr / local / bin / NAME), в первом случае указывается, какой из них использовать.
Мой вопрос: почему первый шебанг предпочтительнее второго?
источник
Ответы:
Это не обязательно лучше.
Преимущество
#!/usr/bin/env python
заключается в том, что он будет использовать любойpython
исполняемый файл, который появляется первым у пользователя$PATH
.Недостаток в
#!/usr/bin/env python
том , что он будет использовать любойpython
исполняемый файл появляется первым в пользователе$PATH
.Это означает, что скрипт может вести себя по-разному в зависимости от того, кто его запускает. Для одного пользователя он может использовать тот,
/usr/bin/python
который был установлен вместе с ОС. Для другого, это могло бы использовать эксперимент/home/phred/bin/python
, который не совсем корректно работает.И если
python
только установлен в/usr/local/bin
, пользователь , который не имеет/usr/local/bin
в$PATH
даже не в состоянии запустить скрипт. (Это вероятно не слишком вероятно в современных системах, но это может легко произойти для более неясного интерпретатора.)Указывая,
#!/usr/bin/python
вы точно указываете, какой интерпретатор будет использоваться для запуска сценария в конкретной системе .Другая потенциальная проблема заключается в том, что
#!/usr/bin/env
уловка не позволяет передавать аргументы интерпретатору (кроме имени сценария, который передается неявно). Это , как правило , не является проблемой, но это может быть. Многие Perl-скрипты написаны с использованием#!/usr/bin/perl -w
, ноuse warnings;
это рекомендуемая замена в наши дни. Сценарии Csh должны использоваться,#!/bin/csh -f
но сценарии csh не рекомендуются в первую очередь. Но могут быть и другие примеры.У меня есть несколько сценариев Perl в персональной системе контроля версий, которые я устанавливаю при настройке учетной записи в новой системе. Я использую
#!
установочный скрипт, который изменяет строку каждого скрипта, так как он устанавливает его в моем$HOME/bin
. (Мне не приходилось использовать ничего, кроме#!/usr/bin/perl
последнего времени; это восходит к временам, когда Perl часто не устанавливался по умолчанию.)Незначительный момент:
#!/usr/bin/env
хитрость, возможно, заключается в злоупотребленииenv
командой, которая изначально была предназначена (как следует из названия) для вызова команды с измененной средой. Кроме того, некоторые старые системы (включая SunOS 4, если я правильно помню) не имелиenv
команды/usr/bin
. Ни один из них, вероятно, не будет серьезной проблемой.env
работает так, многие скрипты используют эту#!/usr/bin/env
хитрость, и поставщики ОС вряд ли что-то предпримут, чтобы ее сломать. Это может быть проблемой, если вы хотите, чтобы ваш скрипт работал на действительно старой системе, но тогда вам, вероятно, придется все равно его изменить.Другая возможная проблема (спасибо Sopalajo de Arrierez за указание на это в комментариях) заключается в том, что задания cron выполняются в ограниченной среде. В частности,
$PATH
как правило, что-то вроде/usr/bin:/bin
. Таким образом, если каталог, содержащий интерпретатор, не находится в одном из этих каталогов, даже если он находится по умолчанию$PATH
в пользовательской оболочке, этот/usr/bin/env
трюк не сработает. Вы можете указать точный путь, или вы можете добавить строку в ваш crontab, чтобы установить$PATH
(man 5 crontab
для деталей).источник
use v5.12;
служит некоторым из этих целей. И#!/usr/bin/env perl5.12
потерпит неудачу, если система имеет Perl 5.14, но не 5.12. Для Python 2 против 3,#!/usr/bin/python2
и#!/usr/bin/python3
, скорее всего, будет работать./usr/local/bin
. Я знаю, что это работает правильно/usr/bin/perl
. Я понятия не имею , работает ли он с темperl
исполняемым файлом, который какой-либо случайный пользователь имеет в своем распоряжении$PATH
. Может быть, кто-то экспериментирует с какой-то древней версией Perl; потому что я указал#!/usr/bin/perl
, мой сценарий (который пользователь не обязательно даже знает или не заботится о сценарии Perl) не перестанет работать./usr/bin/perl
, я выясню это очень быстро, и владелец системы / администратор системы обязан поддерживать его в актуальном состоянии. Если вы хотите запустить мой скрипт со своим собственным perl, не стесняйтесь захватывать и изменять копию или вызывать ее черезperl foo
. (И вы можете подумать о том, что 55 человек, проголосовавших за этот ответ, тоже знают одну или две вещи. Конечно, возможно, что вы правы, и они все неправы, но я бы не поспорил.)Потому что / usr / bin / env может интерпретировать ваш
$PATH
, что делает скрипты более переносимыми.Запустит ваш скрипт, только если python установлен в / usr / local / bin.
Будет интерпретировать ваш
$PATH
, и найти Python в любом каталоге в вашем$PATH
.Таким образом, ваш сценарий более переносим и будет работать без изменений в системах, в которых python установлен как
/usr/bin/python
или/usr/local/bin/python
, или даже в пользовательских каталогах (которые были добавлены$PATH
), например/opt/local/bin/python
.Переносимость - единственная причина, по которой использование
env
предпочтительнее жестко закодированных путей.источник
python
исполняемых файлов особенно распространены по мереvirtualenv
увеличения использования.#! python
, почему это не используется?#! python
не используется, потому что вы должны находиться в том же каталоге, что и двоичный файл python, поскольку голое словоpython
интерпретируется как полный путь к файлу. Если в текущем каталоге нет двоичного файла python, вы получите сообщение об ошибке типаbash: ./script.py: python: bad interpreter: No such file or directory
. Это так же, как если бы вы использовали#! /not/a/real/path/python
Указание абсолютного пути является более точным в данной системе. Недостатком является то, что это слишком точно. Предположим, вы понимаете, что системная установка Perl слишком устарела для ваших сценариев, и вы хотите вместо этого использовать свой собственный: тогда вам нужно отредактировать сценарии и перейти
#!/usr/bin/perl
на#!/home/myname/bin/perl
. Хуже того, если у вас есть Perl/usr/bin
на некоторых машинах,/usr/local/bin
на других и/home/myname/bin/perl
на других машинах, вам придется поддерживать три отдельные копии сценариев и выполнять соответствующую копию на каждой машине.#!/usr/bin/env
ломается, еслиPATH
плохо, но так же почти все. Попытка работать с плохимPATH
очень редко бывает полезна и указывает на то, что вы очень мало знаете о системе, в которой работает скрипт, поэтому вы все равно не можете полагаться на какой-либо абсолютный путь.Есть две программы, на местоположение которых вы можете положиться практически на каждый вариант Unix:
/bin/sh
и/usr/bin/env
. У некоторых непонятных и в основном вышедших на пенсию вариантов Unix/bin/env
не было/usr/bin/env
, но вы вряд ли столкнетесь с ними. Современные системы имеют/usr/bin/env
именно из-за его широкого использования в Шебанге./usr/bin/env
это то, на что вы можете рассчитывать.Кроме того
/bin/sh
, единственный раз, когда вы должны использовать абсолютный путь в шебанге, это когда ваш сценарий не должен быть переносимым, поэтому вы можете рассчитывать на известное место для интерпретатора. Например, можно безопасно использовать скрипт bash, который работает только в Linux#!/bin/bash
. Сценарий, предназначенный только для внутреннего использования, может опираться на соглашения о расположении домашнего переводчика.#!/usr/bin/env
есть минусы. Это более гибко, чем указание абсолютного пути, но все же требует знания имени интерпретатора. Иногда вам может понадобиться запустить интерпретатор, которого нет в$PATH
, например, в месте, относящемся к сценарию. В таких случаях вы часто можете создать сценарий полиглота, который может интерпретироваться как стандартной оболочкой, так и желаемым интерпретатором. Например, чтобы сделать скрипт Python 2 переносимым как на системы, гдеpython
есть Python 3 иpython2
Python 2, так и на системы, гдеpython
есть Python 2 иpython2
не существует:источник
В частности, для Perl использование
#!/usr/bin/env
является плохой идеей по двум причинам.Во-первых, это не портативно. На некоторых малоизвестных платформах env отсутствует в / usr / bin. Во-вторых, как отметил Кит Томпсон , это может вызвать проблемы с передачей аргументов по линии Шебанга. Максимально портативное решение:
Для получения подробной информации о том, как это работает, смотрите perldoc perlrun и что он говорит об аргументе -x.
источник
Причина различия между ними заключается в том, как выполняются сценарии.
Использование
/usr/bin/env
(которое, как упоминалось в других ответах,/usr/bin
на некоторых ОС отсутствует) является обязательным, потому что вы не можете просто поставить имя исполняемого файла после#!
- это должен быть абсолютный путь. Это потому, что#!
механизм работает на более низком уровне, чем оболочка. Это часть двоичного загрузчика ядра. Это можно проверить. Поместите это в файл и отметьте его как исполняемый:Вы обнаружите, что он печатает ошибку, как это, когда вы пытаетесь запустить его:
Если файл помечен как исполняемый и начинается с a
#!
, ядро (которое не знает о$PATH
текущем каталоге или о текущем каталоге: это понятия пользовательской области) будет искать файл, используя абсолютный путь. Поскольку использование абсолютного пути проблематично (как упоминалось в других ответах), кто-то придумал хитрость: вы можете запустить/usr/bin/env
(который почти всегда находится в этом месте), чтобы запустить что-то, используя$PATH
.источник
Есть еще две проблемы с использованием
#!/usr/bin/env
Это не решает проблему указания полного пути к интерпретатору, оно просто перемещает его в
env
.env
не более гарантированно находится в,/usr/bin/env
чемbash
гарантированно находится в/bin/bash
или в Python/usr/bin/python
.env
перезаписывает ARGV [0] именем интерпретатора (например, bash или python).Это предотвращает появление имени вашего скрипта, например, в
ps
выводе (или изменяет, как / где оно появляется) и делает невозможным его поиск, например,ps -C scriptname.sh
[обновление 2016-06-04]
И третья проблема:
Изменение вашего PATH - это больше, чем просто редактирование первой строки скрипта, особенно когда скрипты такого редактирования тривиальны. например:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py
Присоединение или предварительное ожидание каталога к $ PATH довольно простое (хотя вам все равно нужно отредактировать файл, чтобы сделать его постоянным - вашим
~/.profile
или каким-либо другим), и сценарий ЭТОГО редактирования отнюдь не прост, так как PATH можно установить в любом месте скрипта не в первой строке).Изменить порядок каталогов PATH значительно сложнее .... и гораздо сложнее, чем просто редактирование
#!
строки.И у вас все еще есть все другие проблемы, которые
#!/usr/bin/env
дает вам использование.@jlliagre предлагает в комментарии, который
#!/usr/bin/env
полезен для тестирования вашего скрипта с несколькими версиями интерпретатора, «только изменяя их порядок PATH / PATH»Если вам нужно это сделать, гораздо проще просто иметь несколько
#!
строк в верхней части вашего скрипта (это просто комментарии в любом месте, но не самую первую строку) и вырезать / скопировать и вставить ту, которую вы хотите использовать сейчас, чтобы первая строка.Во-первых
vi
, это будет так же просто, как переместить курсор на нужную#!
строку, а затем набратьdd1GP
илиY1GP
. Даже с таким простым редактором, какnano
несколько секунд, мышь можно использовать для копирования и вставки.В целом, преимущества использования
#!/usr/bin/env
минимальны в лучшем случае и, конечно, даже близко не перевешивают недостатки. Даже преимущество «удобства» во многом иллюзорно.ИМО, это глупая идея способствовала определенному виду программиста , который считает , что операционные системы не то , чтобы работали, они являются проблема для работаться вокруг (или игнорируется в лучшем случае ).
PS: вот простой скрипт для изменения интерпретатора нескольких файлов одновременно.
change-shebang.sh
:Запустите его как, например,
change-shebang.sh python2.7 *.py
илиchange-shebang.sh $HOME/bin/my-experimental-ruby *.rb
источник
csh
сценариев продвигает плохое решение: оно работает, но есть гораздо лучшие альтернативы.#!
строку./usr/bin/env
и мне нужно избавиться от него локально, или если они не использовали его, и мне нужно добавить его. Могут возникнуть оба случая, и поэтому сценарии, представленные в этом ответе, являются потенциально полезным инструментом для набора инструментов.Добавив еще один пример здесь:
Использование
env
также полезно, если вы хотите, например, совместно использовать сценарии между несколькимиrvm
средами.Выполнение этого в строке cmd показывает, какая версия ruby будет использоваться при
#!/usr/bin/env ruby
использовании внутри скрипта:env ruby --version
Поэтому, когда вы используете
env
, вы можете использовать разные версии ruby через rvm, не меняя сценарии.источник
Если вы пишете исключительно для себя или для своей работы, и местоположение переводчика, которому вы звоните, всегда находится в одном и том же месте, обязательно используйте прямой путь. Во всех остальных случаях используйте
#!/usr/bin/env
.И вот почему: в вашей ситуации
python
переводчик находился в одном и том же месте независимо от того, какой синтаксис вы использовали, но для многих людей он мог быть установлен в другом месте. Хотя большинство основных программных интерпретаторов находятся во/usr/bin/
многих более новых программах, по умолчанию/usr/local/bin/
.Я бы даже посоветовал всегда использовать,
#!/usr/bin/env
потому что, если у вас установлено несколько версий одного и того же интерпретатора, и вы не знаете, к чему по умолчанию будет работать ваша оболочка, то вам, вероятно, следует это исправить.источник
Из соображений портативности и совместимости лучше использовать
вместо
Существует множество возможностей, когда двоичный файл может находиться в системе Linux / Unix. Проверьте man-страницу hier (7) для подробного описания иерархии файловой системы.
Например, FreeBSD устанавливает все программное обеспечение, которое не является частью базовой системы, в / usr / local / . Поскольку bash не является частью базовой системы, двоичный файл bash устанавливается в / usr / local / bin / bash .
Если вам нужен переносимый скрипт bash / csh / perl / what, который работает в большинстве дистрибутивов Linux и FreeBSD, вы должны использовать #! / Usr / bin / env .
Также обратите внимание, что большинство установок Linux также (жестко) связали двоичный файл env с / bin / env или софт-линклированный / usr / bin с / bin, который не должен использоваться в shebang. Так что не используйте #! / Bin / env .
источник