У заголовку сценарію Bash, яка різниця між цими двома твердженнями:
#!/usr/bin/env bash
#!/usr/bin/bash
Коли я переглянув env
сторінку чоловіка , я отримав таке визначення:
env - run a program in a modified environment
Що це означає?
У заголовку сценарію Bash, яка різниця між цими двома твердженнями:
#!/usr/bin/env bash
#!/usr/bin/bash
Коли я переглянув env
сторінку чоловіка , я отримав таке визначення:
env - run a program in a modified environment
Що це означає?
Відповіді:
Виконання команди наскрізний /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, без необхідності переробляти виконуваний рядок). Але в ситуаціях, коли в центрі уваги є безпека, остання буде кращою, оскільки вона обмежує можливості введення коду.
#!/usr/bin/env echo Hello
скаржиться: /usr/bin/env: echo Hello: No such file or directory
. Мабуть, це трактується echo Hello
як єдиний аргумент /usr/bin/env
.
env
Команда, безумовно, дозволяє передавати аргументи команді. Питання полягає в семантиці #!
рядка, і це залежить від ядра. Останні ядра Linux дозволяють робити такі речі #!/usr/bin/env command args
, але старіші ядра Linux та інші системи цього не дозволяють .
Використання #!/usr/bin/env NAME
змушує пошук оболонки для першої відповідності NAME у змінній середовища $ PATH. Це може бути корисно, якщо ви не знаєте абсолютного шляху або не хочете його шукати.
Замість того, щоб чітко визначити шлях до інтерпретатора як в /usr/bin/bash/
, використовуючи команду env, інтерпретатор шукається та запускається з того місця, де вперше його знайдено. Це має як плюси, так і недоліки
Якщо сценарії оболонки починаються з #!/bin/bash
, вони завжди будуть працювати з bash
з /bin
. Якщо вони однак почати з #!/usr/bin/env bash
, вони будуть шукати bash
в $PATH
і потім почати з першими вони можуть знайти.
Чому це було б корисно? Припустимо, ви хочете запускати bash
сценарії, для яких потрібен bash 4.x або новіший, але у вашій системі встановлено лише bash
3.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.
Я вважаю це корисним, бо коли я не знав про env, перед тим, як почати писати сценарій, я робив це:
type nodejs > scriptname.js #or any other environment
а потім я змінював цей рядок у файлі на shebang.
Я робив це, бо не завжди пам’ятав, де на моєму комп’ютері nodejs - / usr / bin / або / bin /, тому для мене env
це дуже корисно. Можливо, з цим є деталі, але це моя причина