Tại sao nguồn lib / * không hoạt động?


11

Tôi có một chương trình nhỏ chứa cấu trúc thư mục sau:

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

Mỗi tệp chứa một chức năng duy nhất mà tôi sử dụng main.sh.

main.sh:

source lib/*

get_products
clean_products
make_index
test_index

Trong hai chức năng đầu tiên ở trên, nhưng hai chức năng thứ hai thì không.

Tuy nhiên, nếu tôi thay thế source lib/*bằng:

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

Mọi thứ hoạt động như mong đợi.

Bất cứ ai cũng biết tại sao source lib/*không làm việc như mong đợi?


2
Không trả lời câu hỏi, nếu bạn muốn thực hiện nó trong một lớp lót, hãy xem /etc/bashrccách nó sử dụng một forvòng lặp để đối phó /etc/profile.d/*.sh. Nếu bạn tin tưởng nội dung của lib/nó có thể được giảm xuống thành một lớp lót:for i in lib/*.sh; do . "$i"; done
Giàu

Câu trả lời:


21

Nội dung của Bash sourcechỉ có một tên tệp duy nhất:

source filename [arguments]

Bất cứ điều gì ngoài tham số đầu tiên sẽ trở thành một tham số vị trí filename.

Một minh họa đơn giản:

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

Sản lượng đầy đủ của 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.

(Điều này cũng áp dụng cho tích hợp "nguồn chấm" tương đương ., đáng chú ý, là cách POSIX và do đó dễ mang theo hơn.)

Đối với hành vi có vẻ mâu thuẫn mà bạn đang thấy, bạn có thể thử chạy main.sh sau khi thực hiện set -x. Xem những gì báo cáo đang được thực hiện và khi nào có thể cung cấp một đầu mối.


7

Tài liệu Bash chỉ ra rằng sourcehoạt động trên một tên tệp duy nhất :

. (một khoảng thời gian)

. tên tệp [đối số]

Đọc và thực thi các lệnh từ đối số tên tệp trong ngữ cảnh shell hiện tại. Nếu tên tệp ...

Và mã nguồn ... cho nguồn ... sao lưu này:

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

Trường hợp source_fileđược xác định trong evalfile.cđể gọi _evalfile:

rval = _evalfile (filename, flags);

_evalfilechỉ mở một tệp duy nhất:

fd = open (filename, O_RDONLY);

5

Bổ sung câu trả lời hữu ích của lớp b , tôi khuyên bạn không bao giờ nên sử dụng bản mở rộng toàn cầu tham lam nếu bạn không chắc chắn liệu các tệp thuộc loại đang cố gắng mở rộng có ở đó không.

Khi bạn thực hiện bên dưới, có khả năng một tệp (không có .shphần mở rộng) chỉ là một tệp tạm thời chứa một số lệnh có hại (ví dụ rm -rf *) có thể được thực thi (giả sử chúng có quyền thực thi)

source lib/*

Vì vậy, luôn luôn mở rộng toàn cầu với tập ràng buộc thích hợp, trong trường hợp của bạn mặc dù bạn chỉ có thể lặp trên *.shcác tệp một mình

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

Ở đây [ -f "$globFile" ] || continuesẽ quan tâm đến việc quay trở ra khỏi vòng lặp nếu không có mẫu hình cầu nào khớp trong thư mục hiện tại tức là tương đương với các tùy chọn shell mở rộng nullglobtrong bashshell.


Sử dụng thay thế quá trình với catcũng sẽ hoạt động:source <(cat lib/*.sh)
Xophmeister

@Xophmeister, ... cho một giá trị hạn chế hơn cho "công việc". Nếu bạn đã cố gắng gỡ lỗi set -xvà một trong PS4đó đặt BASH_SOURCELINENOtrong nhật ký của bạn, bạn không còn có thể xem tập tin và dòng lệnh nào được đưa ra.
Charles Duffy

2
@Xophmeister, ... cũng vậy, một tập lệnh có thể làm ngắn mạch việc thực thi của nó return. Theo cách làm đó, bất kỳ kịch bản nào làm điều đó sẽ ngăn tất cả những người theo sau thực thi.
Charles Duffy

1
Điều này khá gần với cách nó được thực hiện /etc/bashrckhi nó xử lý /etc/profile.d/*.sh.
Giàu

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.