Cách xác định tập lệnh shell sẽ có nguồn gốc không chạy


40

Tôi đang xác định một tập lệnh shell mà người dùng nên sourcethực hiện.

Có một cách thông thường hoặc thông minh để gợi ý cho người dùng rằng đây là trường hợp, ví dụ thông qua một phần mở rộng tập tin?

Có mã shell nào tôi có thể tự viết trong tệp, điều này sẽ khiến nó lặp lại một tin nhắn và thoát nếu nó được thực thi thay vì nguồn gốc, để tôi có thể giúp người dùng tránh sai lầm rõ ràng này?


1
Vì vậy, nếu người dùng đang viết một tập lệnh shell một dòng x, chỉ chứa lệnh . your-script-to-be-sourced, thì có ổn không, nhưng nếu anh ta muốn thực thi bash your-script-to-be-sourcedthì có nên cấm không? Điểm hạn chế này là gì?
dùng1934428

8
@ user1934428 Tất nhiên rồi. Điều này là bình thường đối với một tập lệnh tính toán một số envbiến và để chúng là đầu ra của tập lệnh thực tế . Một người mới sẽ bị mắc kẹt trong nhiều ngày với câu đố nếu bạn cho phép họ thực thi.
kubanchot

Câu trả lời:


45

Giả sử rằng bạn đang chạy bash, hãy đặt đoạn mã sau gần đầu tập lệnh mà bạn muốn có nguồn gốc nhưng không được thực thi:

if [ "${BASH_SOURCE[0]}" -ef "$0" ]
then
    echo "Hey, you should source this script, not execute it!"
    exit 1
fi

Trong bash, ${BASH_SOURCE[0]}sẽ chứa tên của tệp hiện tại mà trình bao đang đọc bất kể nó có nguồn gốc hay được thực thi.

Ngược lại, $0là tên của tệp hiện tại đang được thực thi.

-efkiểm tra nếu hai tập tin này là cùng một tập tin. Nếu có, chúng tôi cảnh báo người dùng và thoát.

Cả POSIX -efcũng không BASH_SOURCE. Trong khi -efđược hỗ trợ bởi ksh, yash, zsh và Dash, BASH_SOURCEyêu cầu bash. Trongzsh , tuy nhiên, ${BASH_SOURCE[0]}có thể được thay thế bởi ${(%):-%N}.


2
Đơn giản là echo "Usage: source \"$myfile\""
kubanchot

6
@kubanchot sourcekhông phải là hàng xách tay. Xem xét rằng câu trả lời này là dành riêng cho bash, nó không tệ, nhưng đó là một thói quen tốt để sử dụng xách tay.
gronostaj

33

Một tập tin không thể thực thi có thể có nguồn gốc nhưng không được thực thi, vì vậy, như là một tuyến phòng thủ đầu tiên, không đặt cờ thực thi nên là một gợi ý tốt ...

Chỉnh sửa: mẹo tôi vừa vấp ngã: làm cho shebang trở thành bất kỳ thực thi nào không phải là trình thông dịch shell, /bin/falselàm cho tập lệnh trả về lỗi (rc! = 0)

#!/bin/false "This script should be sourced in a shell, not executed directly"

7
Tuy nhiên, một tệp không thể thực thi vẫn có thể được thực thi thông qua ví dụ bash somefile.sh...
twalberg

1
Nếu bạn biết bạn có thể thực thi nó với bash(vd perl, python, awk ...), thì bạn đã xem nguồn và thấy nhận xét nói không làm điều đó :)
xenoid 16/2/18

1
Các tập lệnh Perl thường được đặt tên somefile.plvà Python là somefile.py, vì vậy không, tôi có lẽ đã không đọc các bình luận (đó là những gì, thậm chí, dù sao?) Và bash somefile.shngắn hơn để gõ hơn chmod +x somefile.sh; ./somefile.sh...
twalberg 16/2/18

Ngoài ra, một số shell giống như Bourne, bao gồm bash, trước tiên sẽ thử execvemột tệp, nhưng nếu thất bại, họ kiểm tra tệp theo cách thủ công và giải thích thủ công #!và gọi nó thông qua trình thông dịch đó: đây là một di sản từ thời mà #!không gian hoàn toàn là người dùng quy ước, thay vì được xử lý bởi chính kernel. Tôi nghĩ bash , ít nhất, sẽ không làm điều này cho các tệp không thể thực thi được, nhưng tôi không biết liệu nó có khả dụng để mong đợi hành vi lành mạnh như vậy từ tất cả các trình bao mà người dùng có thể đang gọi tập lệnh từ đó không.
mtraceur

"Nếu bạn biết bạn có thể thực thi nó bằng bash" Ừm, không, đôi khi người dùng không biết rằng có tồn tại các shell khác bash và điều đó bash script.shcó thể nguy hiểm.
Sergiy Kolodyazhnyy

10

Có một số phương pháp được đề xuất trong bài viết Stack Overflow này , trong đó, tôi thích phương pháp dựa trên chức năng được đề xuất bởi WiINA Purwantomr.spuratic :

Cách mạnh mẽ nhất, như được đề xuất bởi WiINA Purwanto, là kiểm tra FUNCNAME[1] trong một chức năng :

function mycheck() { declare -p FUNCNAME; }
mycheck

Sau đó:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

Điều này tương đương với việc kiểm tra đầu ra của caller, các giá trị mainsourcephân biệt bối cảnh của người gọi. Sử dụng FUNCNAME[]giúp bạn nắm bắt và phân tích callerđầu ra. Bạn cần phải biết hoặc tính toán độ sâu cuộc gọi địa phương của bạn để được chính xác mặc dù. Các trường hợp như một tập lệnh được lấy nguồn từ bên trong một chức năng hoặc tập lệnh khác sẽ làm cho mảng (ngăn xếp) sâu hơn. ( FUNCNAMElà một biến mảng bash đặc biệt, nó nên có các chỉ mục liền kề tương ứng với ngăn xếp cuộc gọi, miễn là không bao giờ unset.)

Vì vậy, bạn có thể thêm vào đầu tập lệnh:

function check()
{
    if [[ ${FUNCNAME[-1]} != "source" ]]   # bash 4.2+, use ${FUNCNAME[@]: -1} for older
    then
        printf "Usage: source %s\n" "$0"
        exit 1
    fi
}
check

7

Giả sử nó chỉ là vô dụng, thay vì có hại, để thực thi tập lệnh, bạn có thể thêm

return 0 || printf 'Must be sourced, not executed\n' >&2

đến cuối kịch bản. returnbên ngoài hàm có mã thoát khác không trừ khi tệp đang được lấy nguồn.


3
Lưu ý rằng điều này trả về trạng thái thoát 0. Hãy thử thành ngữ tương tự mà tôi sử dụng thay thế:return 2>/dev/null; echo "$0: This script must be sourced" 1>&2; exit 1
wjandrea

5

Khi bạn lấy một tập lệnh shell, dòng shebang sẽ bị bỏ qua. Bằng cách đặt một shebang không hợp lệ, bạn có thể cảnh báo người dùng rằng tập lệnh đã được thực thi sai:

#!/bin/bash source-this-script
# ...

Thông báo lỗi sẽ là:

/bin/bash: source-this-script: No such file or directory

Tên đối số (tùy ý) đã cung cấp một gợi ý mạnh mẽ, nhưng thông báo lỗi vẫn không rõ ràng 100%. Chúng tôi có thể khắc phục điều này bằng một tập lệnh tiện ích source-this-scriptđược đặt ở đâu đó trong PATH:

#!/bin/sh
echo >&2 "This script must be sourced, not executed${1:+: }${1:-!}"
exit 1

Bây giờ, thông báo lỗi sẽ là:

This script must be sourced, not executed: path/to/script.sh

So sánh với các phương pháp khác

So với các câu trả lời khác, cách tiếp cận này chỉ yêu cầu thay đổi tối thiểu cho mỗi tập lệnh (và có một dòng shebang giúp phát hiện loại tệp trong trình chỉnh sửa và chỉ định phương ngữ tập lệnh shell, do đó thậm chí còn có lợi ích). Nhược điểm là một thông báo lỗi không rõ ràng hoặc bổ sung (một lần) của một tập lệnh shell khác.

Tuy nhiên, điều đó không ngăn cản việc gọi rõ ràng bash path/to/script.sh(cảm ơn @muru!).


1
Một nhược điểm khác là điều này sẽ không bảo vệ chống lại bash some/script.sh, mà cũng sẽ bỏ qua shebang.
muru

4
Bạn có thể làm cho thông điệp rõ ràng hơn bằng cách làm cho shebang #!/bin/echo 'You must source this script!'hoặc một cái gì đó tương tự.
Chris

1
@Chris: Đúng, nhưng sau đó tôi sẽ mất khả năng phát hiện loại tệp (ví dụ như trong Vim) và tài liệu về phương ngữ shell này là gì. Nếu bạn không quan tâm đến những điều này, đề nghị của bạn thực sự sẽ thoát khỏi kịch bản thứ hai!
Ingo Karkat
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.