Почему источник lib / * не работает?

11

У меня есть небольшая программа, которая содержит следующую структуру папок:

- main.sh
- lib/
  - clean.sh
  - get.sh
  - index.sh
  - test.sh

Каждый файл содержит одну функцию, которую я использую в main.sh.

main.sh:

source lib/*

get_products
clean_products
make_index
test_index

Выше первые две функции работают, но вторые две не работают.

И все же, если я заменю source lib/*на:

source lib/get.sh
source lib/clean.sh
source lib/index.sh
source lib/test.sh

Все работает как положено.

Кто-нибудь знает, почему source lib/*не работает, как ожидалось?

Филип Киркбрайд
источник
2
Не отвечая на вопрос, если вы хотите сделать это в одну строку, посмотрите, /etc/bashrcкак он использует forцикл для решения /etc/profile.d/*.sh. Если вы доверяете содержимому, lib/его можно сократить до однострочного:for i in lib/*.sh; do . "$i"; done
Rich

Ответы:

21

Встроенный Bash sourceпринимает только одно имя файла:

source filename [arguments]

Все, что находится за пределами первого параметра, становится позиционным параметром для filename.

Простая иллюстрация:

$ cat myfile
echo "param1: $1"
$ source myfile foo
param1: foo

Полный вывод help source

source: source filename [arguments]

Execute commands from a file in the current shell.

Read and execute commands from FILENAME in the current shell.  The
entries in $PATH are used to find the directory containing FILENAME.
If any ARGUMENTS are supplied, they become the positional parameters
when FILENAME is executed.

Exit Status:
Returns the status of the last command executed in FILENAME; fails if
FILENAME cannot be read.

(Это также относится к эквивалентному встроенному «точечному источнику», .который, стоит отметить, является способом POSIX и, следовательно, более переносимым.)

Что касается кажущегося противоречивого поведения, которое вы видите, вы можете попробовать запустить main.sh после выполнения set -x. Видеть, какие операторы выполняются и когда могут дать подсказку.

B слой
источник
7

Bash документация указывает, что sourceработает с одним именем файла :

, (Период)

, имя файла [аргументы]

Чтение и выполнение команд из аргумента имени файла в текущем контексте оболочки. Если имя файла ...

И исходный код ... для источника ... подтверждает это:

result = source_file (filename, (list && list->next));

Где source_fileопределено в evalfile.cзвонить _evalfile:

rval = _evalfile (filename, flags);

и _evalfileоткрывает только один файл:

fd = open (filename, O_RDONLY);
Джефф Шаллер
источник
5

В дополнение к полезному ответу b-layer , я бы посоветовал никогда не использовать жадное расширение glob, если вы не уверены, существуют ли файлы типа, пытающегося раскрыться.

Когда вы сделали это ниже, существует вероятность того, что файл (не имеющий .shрасширения) будет просто временным файлом, содержащим некоторые вредоносные команды (например rm -rf *), которые могут быть выполнены (при условии, что у них есть разрешения на выполнение)

source lib/*

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

for globFile in lib/*.sh; do
    [ -f "$globFile" ] || continue
    source "$globFile"
done

Здесь [ -f "$globFile" ] || continueон позаботится о возврате из цикла, если в текущей папке не найден шаблон глобуса, то есть эквивалент расширенных параметров оболочки nullglobв bashоболочке.

Inian
источник
Использование процесса замены с catтакже будет работать:source <(cat lib/*.sh)
Xophmeister
@Xophmeister, ... для более ограниченного значения "работа". Если вы попытались выполнить отладку с помощью set -xи a, PS4который помещает BASH_SOURCEи LINENOв ваши журналы, вы больше не видите, из какого файла и строки дается данная команда.
Чарльз Даффи
2
@Xophmeister, ... также сценарий может прервать его выполнение return. Следуя этой практике, любой скрипт, который делает это, будет препятствовать выполнению всех следующих.
Чарльз Даффи
1
Это довольно близко к тому, как это делается /etc/bashrcпри обработке /etc/profile.d/*.sh.
Рич