Яка різниця між "#! / Usr / bin / env bash" та "#! / Usr / bin / bash"?


371

У заголовку сценарію Bash, яка різниця між цими двома твердженнями:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Коли я переглянув env сторінку чоловіка , я отримав таке визначення:

 env - run a program in a modified environment

Що це означає?



6
Хто може мені сказати, чому це питання закрите, "пов'язане з програмуванням або розробкою програмного забезпечення"?
tarrsalah

1
Я погоджуюсь, що це не тематика, але, мабуть, це дублікат кількох інших питань, таких як цей .
Кіт Томпсон

16
Це питання не повинно бути позначене як поза темою. Просто потрібно 5 людей з вище 3000 оцінок, щоб позначити це як "на тему", і його можна буде знову відкрити. Це питання - конкретно про програмування.
Danijel-James W

3
Я в шоці. Шокований, виявивши, що документація на Linux рясніє тавтологіями. xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

Відповіді:


323

Виконання команди наскрізний /usr/bin/envмає перевагу шукає то , що за замовчуванням версія програми знаходиться в поточному окр ironment.

Таким чином, вам не потрібно шукати його в певному місці в системі, оскільки ці шляхи можуть знаходитися в різних місцях в різних системах. Поки він на вашому шляху, він знайде його.

Недоліком є ​​те, що ви не зможете пропустити більше одного аргументу (наприклад, ви не зможете написати /usr/bin/env awk -f), якщо ви хочете підтримати Linux, оскільки POSIX неясний щодо того, як слід інтерпретувати рядок, а Linux інтерпретує все після першого простір для позначення одного аргументу. Ви можете використовувати /usr/bin/env -Sдля деяких версій, envщоб обійти це, але тоді сценарій стане ще менш портативним і зламається на досить недавніх системах (наприклад, навіть Ubuntu 16.04, якщо не пізніше).

Ще один недолік полягає в тому, що оскільки ви не викликаєте явного виконуваного файлу, він має потенціал помилок, а також у проблемах із безпекою багатокористувацьких систем (якщо комусь вдалося отримати їх виконувану програму, наприклад, bashна вашому шляху).

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

У деяких ситуаціях може бути кращим перший (як, наприклад, запуск сценаріїв python з декількома версіями python, без необхідності переробляти виконуваний рядок). Але в ситуаціях, коли в центрі уваги є безпека, остання буде кращою, оскільки вона обмежує можливості введення коду.


22
Ще один недолік полягає в тому, що ви не можете передавати додатковий аргумент перекладачеві.
Кіт Томпсон

1
@KeithThompson: Неправильна інформація. Ви можете передати параметри базовому інтерпретатору за допомогою / usr / bin / env!
Гаурав Агарвал

4
@GauravAgarwal: Не в моїй системі. Сценарій , який містить лише цю одну рядок: #!/usr/bin/env echo Helloскаржиться: /usr/bin/env: echo Hello: No such file or directory. Мабуть, це трактується echo Helloяк єдиний аргумент /usr/bin/env.
Кіт Томпсон

1
@ AndréLaszlo: envКоманда, безумовно, дозволяє передавати аргументи команді. Питання полягає в семантиці #!рядка, і це залежить від ядра. Останні ядра Linux дозволяють робити такі речі #!/usr/bin/env command args, але старіші ядра Linux та інші системи цього не дозволяють .
Кіт Томпсон

1
Чому в блоці коду є зворотні посилання? Чи не повинні їх видаляти?
Бенджамін В.

63

Використання #!/usr/bin/env NAMEзмушує пошук оболонки для першої відповідності NAME у змінній середовища $ PATH. Це може бути корисно, якщо ви не знаєте абсолютного шляху або не хочете його шукати.


9
Принаймні, ви повинні знати, де env :).
ᐅ devrimbaris

1
Відмінна відповідь. Коротко пояснює те, що робить env shebang, а не говорить "вибирає програму на основі конфігурації вашої системи"
De Novo

13

Замість того, щоб чітко визначити шлях до інтерпретатора як в /usr/bin/bash/, використовуючи команду env, інтерпретатор шукається та запускається з того місця, де вперше його знайдено. Це має як плюси, так і недоліки


У більшості систем вони будуть функціонально однаковими, але це залежить від розташування ваших файлів bash та env. Не впевнений, як це вплине на змінні середовища.
сафай

2
"Можна вказати інтерпретатора без використання env, надавши повний шлях до інтерпретатора. Проблема полягає в тому, що в різних комп'ютерних системах точний шлях може бути різним. Замість використання ENV інтерпретатор шукає та знаходиться на час виконання сценарію. Це робить скрипт більш портативним, але також збільшує ризик вибору неправильного інтерпретатора, оскільки він шукає відповідність у кожному каталозі на виконуваному шляху пошуку. Він також страждає від тієї ж проблеми, що шлях до двійкового перегляду env також може бути різним на машині. "- Вікіпедія
Майк Кларк

10

Якщо сценарії оболонки починаються з #!/bin/bash, вони завжди будуть працювати з bashз /bin. Якщо вони однак почати з #!/usr/bin/env bash, вони будуть шукати bashв $PATHі потім почати з першими вони можуть знайти.

Чому це було б корисно? Припустимо, ви хочете запускати bashсценарії, для яких потрібен bash 4.x або новіший, але у вашій системі встановлено лише bash3.x, і в даний час ваш дистрибутив не пропонує більш нової версії або ви не адміністратор і не можете змінити те, що встановлено в цій системі .

Звичайно, ви можете завантажити вихідний код bash і створити власний bash з нуля, розмістивши його, ~/binнаприклад. Ви також можете змінити $PATHзмінну у своєму .bash_profileфайлі, щоб вона була включена ~/binяк перший запис ( PATH=$HOME/bin:$PATHоскільки ~не буде розширюватися в $PATH). Якщо ви зараз зателефонуєте bash, оболонка спочатку шукатиме її $PATHв порядку, тому вона починається з того ~/bin, де вона знайде вашу bash. Те ж саме відбувається, якщо сценарії шукають для bashвикористання #!/usr/bin/env bash, тож тепер ці сценарії працюватимуть у вашій системі за допомогою власної bashзбірки.

Недоліком є ​​те, що це може призвести до несподіваної поведінки, наприклад, один і той же сценарій на одній машині може працювати з різними інтерпретаторами для різних середовищ або користувачів з різними шляхами пошуку, викликаючи різного роду головні болі.

Найбільшим недоліком envє те, що деякі системи дозволять лише один аргумент, тому ви не можете цього зробити #!/usr/bin/env <interpreter> <arg>, оскільки системи будуть розглядатись <interpreter> <arg>як один аргумент (вони будуть трактувати це так, як якщо б вираз був цитований), і таким чином envбуде шукати інтерпретатора з ім'ям <interpreter> <arg>. Зауважте, що це не проблема самої envкоманди, яка завжди дозволяла проходити декілька параметрів, але із сизанг-аналізатором системи, що аналізує цей рядок ще до виклику env. Тим часом це було виправлено у більшості систем, але якщо ваш сценарій хоче бути надто портативним, ви не можете розраховувати, що це було виправлено у системі, яку ви будете працювати.

Це навіть може мати наслідки для безпеки, наприклад, якщо sudoвін не був налаштований на очищення навколишнього середовища або $PATHбув виключений з очищення. Дозвольте мені продемонструвати це:

Зазвичай /binце добре захищене місце, тільки rootтам вдається щось змінити. Ваш домашній каталог не є, проте жодна програма, яку ви запускаєте, не може внести зміни до нього. Це означає, що зловмисний код може помістити підробку bashв якийсь прихований каталог, змінити ваш, .bash_profileщоб включити цей каталог у свій $PATH, так що всі використовувані сценарії #!/usr/bin/env bashв кінцевому підсумку працюватимуть з цією підробкою bash. Якщо sudoтриває $PATH, ви у великій неприємності.

Наприклад, інструмент створює файл ~/.evil/bashіз таким вмістом:

#!/bin/bash

if [ $EUID -eq 0 ]; then
  echo "All your base are belong to us..."
  # We are root - do whatever you want to do
fi

/bin/bash "$@"

Зробимо простий сценарій sample.sh:

#!/usr/bin/env bash

echo "Hello World"

Доказ концепції (у системі, де sudoзберігається $PATH):

$ ./sample.sh
Hello World

$ sudo ./sample.sh
Hello World

$ export PATH="$HOME/.evil:$PATH"

$ ./sample.sh
Hello World

$ sudo ./sample.sh
All your base are belong to us...
Hello World

Зазвичай класичні оболонки повинні розташовуватися всередині, /binі якщо ви не хочете розміщувати їх там з будь-якої причини, насправді не проблема розміщувати симпосилання в таких /binточках, що вказують на їхні реальні місця (або, можливо, /binсаме це є посилання), Я б завжди ходив з #!/bin/shі #!/bin/bash. Просто занадто багато цього може зламатися, якщо вони більше не працюватимуть. Справа не в тому, що POSIX вимагає цієї позиції (POSIX не стандартизує імена шляхів і, отже, навіть не стандартизує функцію shebang), але вони настільки поширені, що навіть якщо система не запропонує /bin/sh, вона, ймовірно, все-таки зрозуміє #!/bin/shі знайте, що з ним робити, і це може бути лише для сумісності з існуючим кодом.

Але для більш сучасних, нестандартних, необов'язкових інтерпретаторів, таких як Perl, PHP, Python або Ruby, насправді не вказано ніде, де вони повинні бути розташовані. Вони можуть бути в , /usr/binале вони так само можуть бути в /usr/local/binабо в абсолютно іншій гілці ієрархії ( /opt/..., /Applications/...і т.д.). Ось чому вони часто використовують #!/usr/bin/env xxxсинтаксис shebang.


4

Я вважаю це корисним, бо коли я не знав про env, перед тим, як почати писати сценарій, я робив це:

type nodejs > scriptname.js #or any other environment

а потім я змінював цей рядок у файлі на shebang.
Я робив це, бо не завжди пам’ятав, де на моєму комп’ютері nodejs - / usr / bin / або / bin /, тому для мене envце дуже корисно. Можливо, з цим є деталі, але це моя причина

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.