Mengapa sumber lib / * tidak berfungsi?

11

Saya memiliki program kecil yang berisi struktur folder berikut:

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

Setiap file berisi satu fungsi yang saya gunakan di main.sh.

main.sh:

source lib/*

get_products
clean_products
make_index
test_index

Di atas dua fungsi pertama bekerja tetapi yang kedua tidak.

Namun jika saya ganti source lib/*dengan:

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

Semuanya berfungsi seperti yang diharapkan.

Adakah yang tahu mengapa source lib/*tidak bekerja seperti yang diharapkan?

Philip Kirkbride
sumber
2
Tidak menjawab pertanyaan, jika Anda ingin melakukannya dalam satu baris, lihat /etc/bashrcbagaimana menggunakan forloop untuk menangani /etc/profile.d/*.sh. Jika Anda memercayai isinya lib/dapat direduksi menjadi satu-liner:for i in lib/*.sh; do . "$i"; done
Kaya

Jawaban:

21

Bash's sourcebuiltin hanya membutuhkan satu nama file:

source filename [arguments]

Apa pun di luar parameter pertama menjadi parameter posisi filename.

Ilustrasi sederhana:

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

Output penuh dari 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.

(Ini juga berlaku untuk built-in "dot source" .yang sepadan, yang patut dicatat, adalah cara POSIX dan karenanya lebih portabel.)

Adapun perilaku yang tampaknya kontradiktif yang Anda lihat, Anda dapat mencoba menjalankan main.sh setelah melakukannya set -x. Melihat pernyataan apa yang dieksekusi dan kapan dapat memberikan petunjuk.

B Layer
sumber
7

Dokumentasi Bash menunjukkan bahwa sourceberfungsi pada satu nama file :

. (periode)

. nama file [argumen]

Baca dan jalankan perintah dari argumen nama file dalam konteks shell saat ini. Jika nama file ...

Dan kode sumber ... untuk sumber ... mencadangkan ini:

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

Di mana source_filedidefinisikan evalfile.cuntuk memanggil _evalfile:

rval = _evalfile (filename, flags);

dan _evalfilehanya membuka satu file:

fd = open (filename, O_RDONLY);
Jeff Schaller
sumber
5

Melengkapi jawaban berguna b-layer , saya sarankan untuk tidak menggunakan ekspansi global yang rakus jika Anda tidak yakin apakah file-file dari tipe yang mencoba untuk memperluas ada di sana.

Ketika Anda melakukannya di bawah ini ada kemungkinan file (tidak memiliki .shekstensi) hanya file sementara yang berisi beberapa perintah berbahaya (misalnya rm -rf *) yang dapat dieksekusi (dengan asumsi mereka memiliki izin yang dijalankan)

source lib/*

Jadi selalu lakukan ekspansi glob dengan set terikat yang tepat, dalam kasus Anda meskipun Anda hanya bisa mengulang pada *.shfile saja

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

Di sini [ -f "$globFile" ] || continueakan menangani kembalinya keluar dari loop jika tidak ada pola bola cocok di folder saat ini yaitu setara dengan opsi shell diperpanjang nullglobdi bashshell.

Inian
sumber
Menggunakan proses substitusi dengan catjuga akan berfungsi:source <(cat lib/*.sh)
Xophmeister
@ Xophmeister, ... untuk nilai yang lebih terbatas untuk "bekerja". Jika Anda mencoba men-debug dengan set -xdan PS4yang menempatkan BASH_SOURCEdan LINENOdi log Anda, Anda tidak bisa lagi melihat file dan baris yang berasal dari perintah yang diberikan.
Charles Duffy
2
@ Xophmeister, ... juga, skrip dapat membuat hubungan pendek dengan eksekusi return. Mengikuti praktik itu, skrip apa pun yang melakukan itu akan mencegah semua yang mengikuti menjalankan.
Charles Duffy
1
Ini cukup dekat dengan bagaimana hal itu dilakukan /etc/bashrcketika diproses /etc/profile.d/*.sh.
Kaya tanggal