Почему лучше использовать “#!/usr/bin/env ИМЯ” вместо “#!/path/to/NAME” как моя хижина?

Команда выполнения whoami это возвратит Вас что-то как этот:

gladimdim tty2        2011-01-27 23:54 (:0)

Полужирным "gladimdim" является пользователем, который был первоначально зарегистрирован к системе.

482
16.07.2015, 03:23
9 ответов

Это не обязательно лучше.

Преимущество #!/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 для деталей).

483
27.01.2020, 19:28
  • 1
    Если/usr/bin/perl является жемчугом 5.8, $HOME/bin/perl 5.12, и сценарий, требующий 5.12 hardcodes/usr/bin/perl в хижинах, это может быть сильная боль для запущения скрипта. Я редко видел, что наличие/usr/bin/env жемчуг захвата жемчуга от ПУТИ проблема, но это часто очень полезно. И это намного более симпатично, чем исполнительный взлом! –  William Pursell 25.01.2012, 22:30
  • 2
    Стоит отметить, что, если Вы хотите использовать определенную версию интерпретатора,/usr/bin/env еще лучше. просто, потому что обычно существует несколько версий интерпретатора, установленных на Вашей машине, названной perl5, perl5.12, perl5.10, python3.3, python3.32, и т.д. и если Ваше приложение было только протестировано на той определенной версии, можно все еще указать #!/usr/bin/env perl5.12 и быть хорошо, даже если пользователю установили его где-нибудь необычный. По моему опыту, 'Python' является обычно просто символьной ссылкой на системную стандартную версию (не обязательно новая версия в системе). –  root 20.07.2013, 22:44
  • 3
    @root: Для Perl, 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
  • 4
    @GoodPerson: Предположим, что я пишу сценарий Perl, который будет установлен в /usr/local/bin. Я, оказывается, знаю, что это работает правильно с /usr/bin/perl. Я понятия не имею, работает ли это со что perl исполняемый файл некоторый случайный пользователь, оказывается, имеет в его $PATH. Возможно, кто-то экспериментирует с некоторой древней версией Perl; потому что я указал #!/usr/bin/perl, мой сценарий (то, которое пользователь не обязательно даже знает или уход, является сценарием Perl) не прекратит работать. –  Keith Thompson 14.12.2013, 23:00
  • 5
    Если это не работает с /usr/bin/perl, Я узнаю очень быстро, и это - системный владелец/администратор обязанность усовершенствовать его. Если Вы хотите запустить мой скрипт со своим собственным жемчугом, не стесняйтесь захватывать и изменять копию или вызывать ее через perl foo. (И Вы могли бы рассмотреть возможность, что 55 человек, которые upvoted этот ответ также знают вещь или два. Конечно, возможно, что Вы правы, и они неправы, но это не способ, которым я держал пари.) –  Keith Thompson 15.12.2013, 00:03

Объективные критерии/требования:

При определении того, следует ли использоватьабсолютныйилилогический(/usr/bin/env)путь к интерпретатору в ше -банге, необходимо учитывать (2 )ключевых соображения:

a)Интерпретатор можно найти в целевой системе

b)правильную версию интерпретатора можно найти в целевой системе

Если мы СОГЛАШАЕМСЯ что "b)" желательно,мы также соглашаемся, что:

c )Предпочтительно, чтобы наши сценариитерпели неудачу , а не выполнялись с использованием неправильной версии интерпретатора и потенциально приводили к противоречивым результатам.

Если мы НЕ СОГЛАСНЫ в том, что " b)" имеет значение, то подойдет любой найденный интерпретатор.

Тестирование:

Поскольку использованиелогическогопути-/usr/bin/envк интерпретатору в узле she -является наиболее расширяемым решением, позволяющим успешно выполнять один и тот же сценарий на целевых хостах с разными путями к одному и тому же интерпретатору, мы проверим его -с помощью Python из-за его популярности -, чтобы увидеть, соответствует ли он нашим критериям.

  1. Живет ли /usr/bin/envв предсказуемой,последовательное расположение на ПОПУЛЯРНЫЕ(не " каждый " )Операционные системы?Да:
  • РЕЛ 7,5
  • Убунту 18.04
  • Распбиан 10 («Бастер»)
  • ОС X 10.15.02
  1. Ниже приведен сценарий Python, выполняемый как внутри, так и снаружи виртуальных оболочек(Pipenv , используемый )во время тестов :
    #!/usr/bin/env pythonX.x
    import sys
    print(sys.version)
    print('Hello, world!')
    
  2. Взрыв -в сценарии был переключен желаемым номером версии Python (, все они установлены на одном хосте):
  • #!/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
  1. Ожидаемые результаты :, что print(sys.version)= env pythonX.x. Каждый раз, когда ./test1.pyвыполнялся с использованием другой установленной версии Python, печаталась правильная версия, указанная в значке -.

  2. Примечания к тестированию:

  • Тесты были ограничены исключительно Python
  • Perl :Подобно Python-ДОЛЖЕН жить в /usr/binв соответствии с FHS
  • Я не пробовал все возможные комбинации для каждого возможного количества операционных систем Linux/Unixy и версий каждой операционной системы.

Заключение:

Хотя ВЕРНО, что #!/usr/bin/env pythonбудет использовать первую версию Python, найденную в пути пользователя, мы можем смягчить это поведение, указав номер версии, например #!/usr/bin/env pythonX.x. Действительно, разработчикам все равно, какой интерпретатор будет найден " первым ", все, что им нужно, это то, чтобы их код выполнялся с использованием указанного интерпретатора, который, как они знают, совместим с их кодом, чтобы обеспечить согласованные результаты -. везде, где это может находиться в файловой системе ...

С точки зрения переносимости/гибкости использованиелогического-/usr/bin/env-вместоабсолютногопути не только удовлетворяет требованиям a ),b )и c )из моего тестирования с различными версиями Python , но также имеет преимущество нечеткой -логики, находящей интерпретатор одной и той же версии, даже если они находятся на разных путях в разных операционных системах. Системы. И хотя БОЛЬШИНСТВО дистрибутивов уважают FHS, не все .

Таким образом, когда сценарий будет FAIL , если двоичный файл находится в другомабсолютномпути, отличном от указанного в shebang, тот же сценарий, использующийлогическийпуть УСПЕШНО , пока не будет найдено совпадение, что обеспечивает большую надежность и расширяемость на разных платформах.

62
20.08.2021, 13:29

Поскольку/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 предпочтен твердым кодированным путям.

103
27.01.2020, 19:28
  • 1
    Это - хороший ответ, однако, он не работает, если связанное монтирование сделано в типе загрузки от /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, не изменяя сценарии.

8
27.01.2020, 19:28
  • 1
    //, Превосходная идея. Смысл нескольких интерпретаторов не должен взламывать код, или иметь код зависят от того определенного интерпретатора. –  Nathan Basanese 15.01.2016, 22:52

Определение полного пути более точно в данной системе. Оборотная сторона - то, что это слишком точно. Предположим, что Вы понимаете, что установка системы 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 …
48
27.01.2020, 19:28

Специально для жемчуга, с помощью #!/usr/bin/env плохая идея по двум причинам.

Во-первых, это не портативно. На некоторых неясных платформах ENV не находится в/usr/bin. Во-вторых, как Keith Thompson отметил, это может доставить неприятности с передающими аргументами на строке хижины. Максимально портативное решение - это:

#!/bin/sh
exec perl -x "$0" "$@"
#!perl

Для получения дополнительной информации о том, как это работает, см. 'perldoc perlrun' и что это говорит о-x аргументе.

23
27.01.2020, 19:28
  • 1
    Точно anser я искал: то, как записать porable "хижину" для сценария жемчуга, который позволит дополнительный aruments, передало жемчугу (последняя строка в Вашем примере принимает дополнительный aruments). –  AmokHuginnsson 26.05.2015, 00:23

Причина различия между ними заключается в том, как выполняются сценарии.

Использование /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.

15
27.01.2020, 19:28

Если вы пишете чисто для себя или для своей работы, и местоположение переводчика, которое вы звоните, всегда в том же месте, все средства используют прямой путь. Во всех других случаях используйте #! / USR / BIN / ENV .

Вот почему: в вашей ситуации Python Python интерпретатор был таким же место, независимо от того, какой синтаксис вы использовали, но для многих людей он мог установить в другом месте. Хотя большинство основных переводчиков программирования расположены в / USR / BIN / , много нового программного обеспечения по умолчанию для / usr / local / bin / .

Я бы даже поспорил всегда использовать #! / USR / BIN / ENV , потому что если у вас есть несколько версий одного и того же интерпретатора, и вы не знаете, что ваша оболочка будет по умолчанию, то вы Вероятно, следует это исправить.

4
27.01.2020, 19:28

Есть еще две проблемы с использованием #!/usr/bin/env

  1. Это не решает проблему указания полного пути к интерпретатору, а просто перемещает его в env.

    env не более гарантированно находится в /usr/bin/env, чем bash гарантированно находится в /bin/bash или python в /usr/bin/python.

  2. env перезаписывает ARGV[0] именем интерпретатора (например, bash или python).

    Это предотвращает появление имени вашего скрипта в выводе, например, ps (или изменяет, как/где оно появляется) и делает невозможным его поиск, например, ps -C scriptname.sh

[update 2016-06-04]

И третья проблема:

  1. Изменение 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

15
29.04.2021, 00:56

Теги

Похожие вопросы