Чому джерело 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
Багатий

Відповіді:


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. Побачити, які заяви виконуються і коли може бути зрозумілим.


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-шару , я б запропонував ніколи не використовувати жадібне розширення глобусу, якщо ви не впевнені, чи є файли типу, що намагаються розгорнути.

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

source lib/*

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

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

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


Використання процесу заміщення catтакож спрацювало б:source <(cat lib/*.sh)
Xophmeister

@Xophmeister, ... для більш обмеженого значення для "роботи". Якщо ви спробували налагоджувати set -xі, PS4що додає BASH_SOURCEі LINENOв свої журнали, ви більше не можете бачити, з якого файлу та рядка походить дана команда.
Чарльз Даффі

2
@Xophmeister, ... також, сценарій може коротко замикати його виконання return. Дотримуючись цієї практики, будь-який сценарій, який би заважав виконувати всі наступні сценарії .
Чарльз Даффі

1
Це досить близько до того, як це робиться в процесі /etc/bashrcйого обробки /etc/profile.d/*.sh.
Багатий

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