Есть ли разница между «.» И «source» в bash, в конце концов?


38

Я искал разницу между "." и встроенные команды «source» и несколько источников (например, в этом обсуждении и на странице руководства bash ) предполагают, что они одинаковы.

Однако после проблемы с переменными среды я провел тест. Я создал файл, 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

[обратите внимание, что первая форма вернула пустую строку]

Таким образом, этот маленький эксперимент показывает , что есть разница в конце концов, где для команды «источника», дочерние среды наследуют все переменные от родителя одного, где за «» это не.

Я что-то упустил, или это недокументированная / устаревшая особенность 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 (правильно)

Кстати, обратите внимание, что когда вы вызываете исполняемый файл по имени, как указано выше (а не со встроенными модулями .или sourceshell), то, какая программа оболочки используется для ее запуска, обычно не определяется тем, из какой оболочки вы ее запускаете , Вместо:

  • Для двоичных файлов ядро ​​может быть настроено для запуска файлов этого конкретного типа. Он проверяет первые два байта файла на «магическое число», которое указывает, какой это двоичный исполняемый файл. Вот как исполняемые двоичные файлы могут работать.

    Это, конечно, чрезвычайно важно, потому что скрипт не может работать без оболочки или другого интерпретатора, который является исполняемым двоичным файлом! Кроме того, многие команды и приложения являются скомпилированными двоичными файлами, а не скриптами.

    ( #!является текстовым представлением «магического числа», указывающего на исполняемый текст.)

  • Для файлов, которые должны работать в оболочке или другом интерпретируемом языке, первая строка выглядит следующим образом:

    #!/bin/sh

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

    #!/usr/bin/python

    Эти строки называются hashbang, shebang и рядом других похожих имен. Посмотрите эту запись FOLDOC , эту статью в Википедии и прочитал ли интерпретатор #! / Bin / sh? за дополнительной информацией.

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

    * Руководство по GNU Bash , 3.7.2 Поиск и выполнение команд : «Если это выполнение завершится неудачно, потому что файл не в исполняемом формате, и файл не является каталогом, предполагается, что это сценарий оболочки, а оболочка выполняет его, как описано в сценариях оболочки ".


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, что отличается. Но это не использует .встроенное; вместо этого, .это часть пути.)
Элия ​​Каган

О, я не внимательно прочитал. [Пробел] файл. Спасибо тебе, мач.
m3nda

13

Да, ты что-то упустил.

Я думаю, что вы путаете '.' это означает текущий каталог, как в ./testenv.shи '.' это означает source(что является встроенной командой). Так и в случае, когда «.» значит sourceбудет . ./testenv.sh. Есть смысл?

Так что попробуйте это:

MY_VAR=12345 
. ./testenv.sh

2
Он ./сообщает ему, где именно файл, без него, bash сначала просматривает PATH, а затем пробует текущий каталог, если не нашел его. Если bash работает в режиме POSIX, и вы не указали путь к файлу (например ./), он будет искать только в PATH и не сможет найти файл, если текущий каталог не в PATH.
Гейра

@geirha Да, вы правы, source.) на самом деле сначала будете проверять $ PATH, даже если они не совсем запускают скрипт в обычном смысле. Мой (бывший) комментарий был неверным.
Элия ​​Каган

Коротко и в точку +1
Дэвид Моралес
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.