Команда выполнения whoami
это возвратит Вас что-то как этот:
gladimdim tty2 2011-01-27 23:54 (:0)
Полужирным "gladimdim" является пользователем, который был первоначально зарегистрирован к системе.
Это не обязательно лучше.
Преимущество #!/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 для указания на него в комментариях) то, что задания крона, выполненные с ограниченной средой. В частности, $PATH
обычно что-то как /usr/bin:/bin
. Таким образом, если каталог, содержащий интерпретатор, оказывается, не находится в одном из тех каталогов, даже если это находится в Вашем значении по умолчанию $PATH
в пользовательской оболочке, затем /usr/bin/env
прием не собирается работать. Можно указать точный тракт, или можно добавить строку к crontab для установки $PATH
(man 5 crontab
для деталей).
При определении того, следует ли использоватьабсолютныйилилогический(/usr/bin/env
)путь к интерпретатору в ше -банге, необходимо учитывать (2 )ключевых соображения:
a)Интерпретатор можно найти в целевой системе
b)правильную версию интерпретатора можно найти в целевой системе
Если мы СОГЛАШАЕМСЯ что "b)" желательно,мы также соглашаемся, что:
c )Предпочтительно, чтобы наши сценариитерпели неудачу , а не выполнялись с использованием неправильной версии интерпретатора и потенциально приводили к противоречивым результатам.
Если мы НЕ СОГЛАСНЫ в том, что " b)" имеет значение, то подойдет любой найденный интерпретатор.
Поскольку использованиелогическогопути-/usr/bin/env
к интерпретатору в узле she -является наиболее расширяемым решением, позволяющим успешно выполнять один и тот же сценарий на целевых хостах с разными путями к одному и тому же интерпретатору, мы проверим его -с помощью Python из-за его популярности -, чтобы увидеть, соответствует ли он нашим критериям.
/usr/bin/env
в предсказуемой,последовательное расположение на ПОПУЛЯРНЫЕ(не " каждый " )Операционные системы?Да:#!/usr/bin/env pythonX.x
import sys
print(sys.version)
print('Hello, world!')
#!/usr/bin/env python2
#!/usr/bin/env python2.7
#!/usr/bin/env python3
#!/usr/bin/env python3.5
#!/usr/bin/env python3.6
#!/usr/bin/env python3.7
Ожидаемые результаты :, что print(sys.version)
= env pythonX.x
. Каждый раз, когда ./test1.py
выполнялся с использованием другой установленной версии Python, печаталась правильная версия, указанная в значке -.
Примечания к тестированию:
/usr/bin
в соответствии с FHSХотя ВЕРНО, что #!/usr/bin/env python
будет использовать первую версию Python, найденную в пути пользователя, мы можем смягчить это поведение, указав номер версии, например #!/usr/bin/env pythonX.x
. Действительно, разработчикам все равно, какой интерпретатор будет найден " первым ", все, что им нужно, это то, чтобы их код выполнялся с использованием указанного интерпретатора, который, как они знают, совместим с их кодом, чтобы обеспечить согласованные результаты -. везде, где это может находиться в файловой системе ...
С точки зрения переносимости/гибкости использованиелогического-/usr/bin/env
-вместоабсолютногопути не только удовлетворяет требованиям a ),b )и c )из моего тестирования с различными версиями Python , но также имеет преимущество нечеткой -логики, находящей интерпретатор одной и той же версии, даже если они находятся на разных путях в разных операционных системах. Системы. И хотя БОЛЬШИНСТВО дистрибутивов уважают FHS, не все .
Таким образом, когда сценарий будет FAIL , если двоичный файл находится в другомабсолютномпути, отличном от указанного в shebang, тот же сценарий, использующийлогическийпуть УСПЕШНО , пока не будет найдено совпадение, что обеспечивает большую надежность и расширяемость на разных платформах.
Поскольку/usr/bin/env может интерпретировать Ваш $PATH
, который делает сценарии более портативными.
#!/usr/local/bin/python
Только запустит Ваш скрипт, если Python будет установлен в/usr/local/bin.
#!/usr/bin/env python
Интерпретирует Ваш $PATH
, и найдите Python в любом каталоге в Вашем $PATH
.
Таким образом, Ваш сценарий является более портативным, и будет работать без модификации над системами, где Python установлен как /usr/bin/python
, или /usr/local/bin/python
, или даже пользовательские каталоги (которые были добавлены к $PATH
), как /opt/local/bin/python
.
Мобильность является единственной причиной, использующей env
предпочтен твердым кодированным путям.
/etc/fstab
следующим образом. fstab имеет: /my/dir /home none bind 0 0
; но $ grep /home /proc/self/mountinfo
урожаи 20 11 8:7 /home /home rw,noatime - ext4 /dev/sda7 rw,data=ordered
и $ findmnt /home
говорит /home /dev/sda7[/home] ext4 rw,noatime,data=ordered
---------121 каталог--------107654----Custom для python
исполняемые файлы особенно распространены как virtualenv
увеличения использования. файлы
– Boycott SE for Monica Cellio
22.11.2013, 02:01
Добавление другого примера здесь:
Используя env
также полезно, когда Вы хотите совместно использовать сценарии между несколькими rvm
среды, например.
Выполнение этого на cmd строке, шоу, какая рубиновая версия будет использоваться когда #!/usr/bin/env ruby
используется в сценарии:
env ruby --version
Поэтому, когда Вы используете env
, можно использовать различные рубиновые версии через rvm, не изменяя сценарии.
Определение полного пути более точно в данной системе. Оборотная сторона - то, что это слишком точно. Предположим, что Вы понимаете, что установка системы 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
, единственное время необходимо использовать полный путь в хижине, - когда сценарий не предназначен, чтобы быть портативным, таким образом, можно рассчитывать на известное местоположение для интерпретатора. Например, сценарий удара, который только работает над Linux, может безопасно использовать #!/bin/bash
. Сценарий, который только предназначен, чтобы использоваться внутренний, может полагаться на конвенции местоположения интерпретатора дома.
#!/usr/bin/env
действительно имеет оборотные стороны. Это более гибко, чем определение полного пути, но все еще требует знания названия интерпретатора. Иногда Вы могли бы хотеть выполнить интерпретатор, который не находится в $PATH
, например, в месте относительно сценария. В таких случаях можно часто делать многоязычный сценарий, который может быть интерпретирован и стандартной оболочкой и желаемым интерпретатором. Например, для создания сценария Python 2 портативным оба к системам, где python
Python 3 и python2
Python 2, и к системам где python
Python 2 и python2
не существует:
#!/bin/sh
''':'
if type python2 >/dev/null 2>/dev/null; then
exec python2 "$0" "$@"
else
exec python "$0" "$@"
fi
'''
# real Python script starts here
def …
Специально для жемчуга, с помощью #!/usr/bin/env
плохая идея по двум причинам.
Во-первых, это не портативно. На некоторых неясных платформах ENV не находится в/usr/bin. Во-вторых, как Keith Thompson отметил, это может доставить неприятности с передающими аргументами на строке хижины. Максимально портативное решение - это:
#!/bin/sh
exec perl -x "$0" "$@"
#!perl
Для получения дополнительной информации о том, как это работает, см. 'perldoc perlrun' и что это говорит о-x аргументе.
Причина различия между ними заключается в том, как выполняются сценарии.
Использование /usr/bin/env
(который, как упоминалось в других ответах, не находится в /usr/bin
на некоторых операционных системах) необходимо, потому что вы не можете просто поставить имя исполняемого файла после #!
- это должен быть абсолютный путь. Это потому, что #!
работает на более низком уровне, чем оболочка. Он является частью двоичного загрузчика ядра. Это можно проверить. Поместите это в файл и пометьте его как исполняемый:
#!bash
echo 'foo'
Вы увидите, что он печатает подобную ошибку при попытке запуска:
Failed to execute process './test.sh'. Reason:
The file './test.sh' does not exist or could not be executed.
Если файл помечен как исполняемый и начинается с #!
, ядро (которое не знает ни о $PATH
, ни о текущем каталоге: это пользовательские концепции) будет искать файл, используя абсолютный путь. Поскольку использование абсолютного пути проблематично (как упоминалось в других ответах), кто-то придумал трюк: Вы можете запустить /usr/bin/env
(который почти всегда находится в этом месте), чтобы запустить что-нибудь, используя $PATH
.
Если вы пишете чисто для себя или для своей работы, и местоположение переводчика, которое вы звоните, всегда в том же месте, все средства используют прямой путь. Во всех других случаях используйте #! / USR / BIN / ENV
.
Вот почему: в вашей ситуации Python Python
интерпретатор был таким же место, независимо от того, какой синтаксис вы использовали, но для многих людей он мог установить в другом месте. Хотя большинство основных переводчиков программирования расположены в / USR / BIN /
, много нового программного обеспечения по умолчанию для / usr / local / bin /
.
Я бы даже поспорил всегда использовать #! / USR / BIN / ENV
, потому что если у вас есть несколько версий одного и того же интерпретатора, и вы не знаете, что ваша оболочка будет по умолчанию, то вы Вероятно, следует это исправить.
Есть еще две проблемы с использованием #!/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
[update 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
в лучшем случае минимальны, и уж точно даже близко не перевешивают недостатки. Даже преимущество "удобства" в значительной степени иллюзорно.
IMO, это глупая идея, продвигаемая определенным типом программистов, которые считают, что операционные системы - это не то, с чем нужно работать, это проблема, которую нужно обходить (или в лучшем случае игнорировать).
PS: вот простой скрипт для изменения интерпретатора сразу нескольких файлов.
change-shebang.sh
:
#!/bin/bash
interpreter="$1"
shift
if [ -z "$(type -P $interpreter)" ] ; then
echo "Error: '$interpreter' is not executable." >&2
exit 1
fi
if [ ! -d "$interpreter" ] && [ -x "$interpreter" ] ; then
shebang='#!'"$(realpath -e $interpreter)" || exit 1
else
shebang='#!'"$(type -P $interpreter)"
fi
for f in "$@" ; do
printf "%s\n" 1 i "$shebang" . w | ed "$f"
done
Запустите его, например, как change-shebang.sh python2.7 *.py
или change-shebang.sh $HOME/bin/my-experimental-ruby *.rb
use v5.12;
подачи часть той цели. И#!/usr/bin/env perl5.12
перестанет работать, если система будет иметь Perl 5.14, но не 5.12. Для Python 2 по сравнению с 3,#!/usr/bin/python2
и#!/usr/bin/python3
вероятно, будут работать. – Keith Thompson 21.07.2013, 00:44/usr/local/bin
. Я, оказывается, знаю, что это работает правильно с/usr/bin/perl
. Я понятия не имею, работает ли это со чтоperl
исполняемый файл некоторый случайный пользователь, оказывается, имеет в его$PATH
. Возможно, кто-то экспериментирует с некоторой древней версией Perl; потому что я указал#!/usr/bin/perl
, мой сценарий (то, которое пользователь не обязательно даже знает или уход, является сценарием Perl) не прекратит работать. – Keith Thompson 14.12.2013, 23:00/usr/bin/perl
, Я узнаю очень быстро, и это - системный владелец/администратор обязанность усовершенствовать его. Если Вы хотите запустить мой скрипт со своим собственным жемчугом, не стесняйтесь захватывать и изменять копию или вызывать ее черезperl foo
. (И Вы могли бы рассмотреть возможность, что 55 человек, которые upvoted этот ответ также знают вещь или два. Конечно, возможно, что Вы правы, и они неправы, но это не способ, которым я держал пари.) – Keith Thompson 15.12.2013, 00:03