Чи є різниця між "." Та "джерелом" у bash?


38

Я шукав різницю між "". і "джерело" вбудованих команд та декілька джерел (наприклад, у цій дискусії та " bash manpage") припускають, що вони точно такі самі.

Однак після проблеми зі змінними середовища я провів тест. Я створив файл, testenv.shякий містить:

#!/bin/bash
echo $MY_VAR

У командному рядку я виконав наступне:

> chmod +x testenv.sh
> MY_VAR=12345
> ./testenv.sh

> source testenv.sh
12345
> MY_VAR=12345 ./testenv.sh
12345

[зауважте, що 1-а форма повернула порожній рядок]

Отже, цей маленький експеримент говорить про те, що все-таки є різниця, коли для команди "source" дочірнє середовище успадковує всі змінні від батьківського, де для ". це не.

Я щось пропускаю, чи це недокументована / застаріла особливість bash ?

[GNU bash, версія 4.1.5 (1) -випуск (x86_64-pc-linux-gnu)]

Відповіді:


68

Коротка відповідь

У вашому запитанні друга команда не використовує ні .вбудовану оболонку, ні sourceвбудовану. Натомість ви фактично запускаєте скрипт в окремій оболонці, викликаючи його по імені, як і у будь-якому іншому виконуваному файлі. Це дає йому окремий набір змінних (хоча якщо ви експортуєте змінну в її батьківську оболонку, вона буде змінною середовища для будь-якого дочірнього процесу , а тому буде включена в дочірніми змінними оболонки ). Якщо ви зміните /пробіл, то це запустить його із .вбудованим, що еквівалентно source.

Розширене пояснення

Це синтаксис sourceвбудованої оболонки, який виконує вміст скрипту в поточній оболонці (і, таким чином, зі змінними поточної оболонки):

source testenv.sh

Це синтаксис .вбудованого, який робить те саме, що source:

. testenv.sh

Однак цей синтаксис запускає скрипт як виконуваний файл, запускаючи нову оболонку для його запуску:

./testenv.sh

Це не використання .вбудованого. Швидше, .це частина шляху до файлу, який ви виконуєте. Взагалі кажучи, ви можете запустити будь-який виконуваний файл в оболонці, викликавши його ім'ям, що містить принаймні один /символ. Таким чином, запустити файл у поточному каталозі, передуючи цьому, ./це найпростіший спосіб. Якщо у вашому поточному каталозі немає PATH, ви не можете запустити скрипт із командою testenv.sh. Це дозволяє запобігти випадковому виконанню файлів у поточному каталозі, коли вони мають намір виконати системну команду чи інший файл, який існує в деякому каталозі, переліченому в PATHзмінній оточення.

Оскільки запуск файлу на ім'я (а не з sourceабо .) запускає його в новій оболонці, у нього буде власний набір змінних оболонок. Нова оболонка наслідує змінні середовища від процесу виклику (що в даному випадку є вашою інтерактивною оболонкою), і ці змінні середовища стають змінними оболонки в новій оболонці. Однак, щоб змінна оболонки була передана новій оболонці, має бути таким:

  1. Експортувала змінну оболонки, внаслідок чого вона була змінною середовища. Використовуйте для цього exportвбудовану оболонку. У вашому прикладі ви можете використовувати export MY_VAR=12345для встановлення та експорту змінної за один крок, або якщо вона вже встановлена, ви можете просто використовувати export MY_VAR.

  2. Змінна оболонки явно встановлюється і передається для команди, яку ви виконуєте, внаслідок чого вона є змінною середовища протягом тривалості запущеної команди. Це , як правило , виконує наступне :

    MY_VAR=12345 ./testenv.sh

    Якщо MY_VARце змінна оболонка, яка не була експортована, ви навіть можете запустити testenv.shз MY_VARпереданою як змінну середовища, встановивши її на себе :

    MY_VAR="$MY_VAR" ./testenv.sh

./ Синтаксис для сценаріїв вимагає, щоб лінія Hashbang працювала (правильно)

До речі, зауважте, що коли ви викликаєте виконуваний файл на ім'я, як зазначено вище (а не з вбудованими .або sourceоболонками), яка програма оболонки використовується для його запуску, як правило, не визначається, з якої оболонки ви її запустите . Замість цього:

  • Для бінарних файлів ядро ​​може бути налаштоване для запуску файлів конкретного типу. Він вивчає перші два байти файлу на "магічне число", яке вказує, який це двійковий виконуваний файл. Це те, як виконуються файли бінарних файлів.

    Це, звичайно, надзвичайно важливо, тому що скрипт не може працювати без оболонки чи іншого інтерпретатора, який є виконуваним двійковим файлом! Крім того, багато команд і додатків складаються бінарними файлами, а не скриптами.

    ( #!це текстове подання "магічного числа" із зазначенням тексту, що виконується.)

  • Для файлів, які повинні працювати в оболонці чи іншій інтерпретованій мові, перший рядок виглядає так:

    #!/bin/sh

    /bin/shможе бути замінено будь-якою іншою оболонкою або інтерпретатором, призначеною для запуску програми. Наприклад, програма Python може починатися з рядка:

    #!/usr/bin/python

    Ці рядки називаються хашбан, шебанг та ряд інших подібних назв. Дивіться цей запис FOLDOC , цю статтю у Вікіпедії та чи читає перекладач #! / Bin / sh? для отримання додаткової інформації.

  • Якщо текстовий файл позначений виконуваним, і ви запускаєте його зі своєї оболонки (як ./filename), але він не починається з цього #!, ядро ​​не виконує його. Однак, побачивши, що це сталося, ваша оболонка спробує запустити її, передавши своє ім'я якійсь оболонці. Є кілька вимог , вміщених на те , що оболонку , яка є ( «оболонка повинні виконати команду , еквівалентну має оболонку виклик ...» ). На практиці деякі оболонки - в тому числі bash* - виконують ще один екземпляр, а інші використовують/bin/sh. Я настійно рекомендую вам цього не уникати і використовувати натомість рядок хешбангу (або запустити сценарій, передавши його потрібному інтерпретатору, наприклад, bash filename).

    * Посібник GNU Bash , 3.7.2 Пошук та виконання команд : "Якщо це виконання не вдалося, оскільки файл не має виконуваного формату, а файл не є каталогом, передбачається, що це сценарій оболонки, і оболонка виконує його, як описано у скриптах Shell ".


2
Що мені здалося корисним source- це те, що функції стали доступними з bash, не потребуючи завантаження чи запуску заново. Приклад #!/bin/bash function olakease {echo olakease;}. Завантаживши його, source file.shви можете безпосередньо зателефонувати olakeaseз bash. Мені це дуже подобається. Після цього джерело завантажує багато речей, крапка .призначена лише для виконання і є чимось на зразок використанняbash file.sh
m3nda

2
@ erm3nda .має таку поведінку: . file.shі source file.shвиконайте абсолютно те саме, включаючи збереження функцій, визначених у file.sh. (Можливо, ви думаєте про те ./file.sh, що відрізняється. Але це не використовує .вбудований; натомість .це частина шляху.)
Елія Каган

О, я не прочитав уважно файл. [Space]. Дякую тобі, мач.
м3нда

13

Так, щось вам не вистачає.

Я думаю, що ви плутаєте "." це означає поточний каталог, як в ./testenv.shі '.' це означає source(що є вбудованою командою). Так у випадку, коли '.' значить, sourceбуло б . ./testenv.sh. Мати сенс?

Тому спробуйте це:

MY_VAR=12345 
. ./testenv.sh

2
./Розповідає , де саме файл вона, без нього, Баш буде виглядати через PATH, а потім спробуйте поточну директорію , якщо вона не знайшла його. Якщо bash працює в режимі POSIX, і ви не надаєте шлях до файлу (наприклад ./), він здійснюватиме пошук лише в PATH та не зможе знайти файл, якщо поточний dir не знаходиться в PATH.
geirha

@geirha Так, ти маєш рацію, source.) спочатку перевіриш $ PATH, навіть якщо вони насправді не виконують сценарій у звичайному розумінні. Мій (колишній) коментар був невірним.
Елія Каган

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