Trong các trường hợp phổ biến nhất, $0
sẽ chứa một đường dẫn, tuyệt đối hoặc liên quan đến tập lệnh, vì vậy
script_path=$(readlink -e -- "$0")
(giả sử có một readlink
lệnh và nó hỗ trợ -e
) nói chung là một cách đủ tốt để có được đường dẫn tuyệt đối chính tắc đến tập lệnh.
$0
được gán từ đối số chỉ định tập lệnh được truyền cho trình thông dịch.
Ví dụ: trong:
the-shell -shell-options the/script its args
$0
được the/script
.
Khi bạn chạy:
the/script its args
Vỏ của bạn sẽ làm một:
exec("the/script", ["the/script", "its", "args"])
Nếu tập lệnh chứa một #! /bin/sh -
she-bang chẳng hạn, hệ thống sẽ chuyển đổi nó thành:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(nếu nó không chứa tiếng nổ hoặc nói chung là nếu hệ thống trả về lỗi ENOEXEC, thì vỏ của bạn sẽ làm điều tương tự)
Có một ngoại lệ cho tập lệnh setuid / setgid trên một số hệ thống, trong đó hệ thống sẽ mở tập lệnh trên một số fd
x
và chạy thay thế:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
để tránh các điều kiện chủng tộc (trong trường hợp $0
này sẽ chứa /dev/fd/x
).
Bây giờ, bạn có thể lập luận rằng đó /dev/fd/x
là một đường dẫn đến kịch bản đó. Tuy nhiên, lưu ý rằng nếu bạn đọc từ $0
, bạn sẽ phá vỡ tập lệnh khi bạn sử dụng đầu vào.
Bây giờ, có một sự khác biệt nếu tên lệnh script được gọi không chứa dấu gạch chéo. Trong:
the-script its args
Vỏ của bạn sẽ nhìn lên the-script
trong $PATH
. $PATH
có thể chứa đường dẫn tuyệt đối hoặc tương đối (bao gồm cả chuỗi rỗng) đến một số thư mục. Chẳng hạn, nếu $PATH
chứa /bin:/usr/bin:
và the-script
được tìm thấy trong thư mục hiện tại, shell sẽ thực hiện:
exec("the-script", ["the-script", "its", "args"])
mà sẽ trở thành:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
Hoặc nếu nó được tìm thấy trong /usr/bin
:
exec("/usr/bin/the-script", ["the-script", "its", "args"])
exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
"-", "/usr/bin/the-script", "its", "args")
Trong tất cả các trường hợp trên trừ trường hợp góc setuid, $0
sẽ chứa đường dẫn (tuyệt đối hoặc tương đối) đến tập lệnh.
Bây giờ, một tập lệnh cũng có thể được gọi là:
the-interpreter the-script its args
Khi the-script
như trên không chứa các ký tự gạch chéo, hành vi thay đổi một chút từ vỏ này sang vỏ khác.
Các ksh
triển khai AT & T cũ thực sự đang tìm kiếm tập lệnh vô điều kiện $PATH
(đây thực sự là một lỗi và lỗ hổng bảo mật cho tập lệnh setuid), vì vậy $0
thực sự không có đường dẫn đến tập lệnh trừ khi việc $PATH
tìm kiếm thực sự tìm thấy the-script
trong thư mục hiện tại.
AT & T mới hơn ksh
sẽ thử và diễn giải the-script
trong thư mục hiện tại nếu có thể đọc được. Nếu không nó sẽ tìm kiếm một có thể đọc và thực thi the-script
trong $PATH
.
Đối với bash
, nó kiểm tra nếu the-script
là trong thư mục hiện tại (và không phải là một liên kết tượng trưng tấm) và nếu không, tra cứu cho một thể đọc được (không nhất thiết phải thực thi) the-script
trong $PATH
.
zsh
trong sh
thi đua sẽ làm như bash
trừ rằng nếu the-script
là một liên kết tượng trưng vỡ trong thư mục hiện, nó sẽ không tìm kiếm một the-script
trong $PATH
và thay vào đó sẽ báo cáo một lỗi.
Tất cả các vỏ giống như Bourne khác không nhìn the-script
lên $PATH
.
Đối với tất cả các shell đó, nếu bạn thấy rằng $0
nó không chứa a /
và không thể đọc được, thì có lẽ nó đã được tra cứu $PATH
. Sau đó, vì các tệp trong $PATH
có khả năng được thực thi, nên có thể sử dụng xấp xỉ an toàn command -v -- "$0"
để tìm đường dẫn của nó (mặc dù điều đó sẽ không hoạt động nếu $0
cũng là tên của shell dựng sẵn hoặc từ khóa (trong hầu hết các shell)).
Vì vậy, nếu bạn thực sự muốn bao gồm cho trường hợp đó, bạn có thể viết nó:
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}""; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
(phần ""
bổ sung $PATH
là để bảo toàn phần tử rỗng có dấu với các lớp vỏ có $IFS
vai trò là dấu phân cách thay vì dấu phân cách ).
Bây giờ, có nhiều cách bí truyền hơn để gọi một kịch bản. Người ta có thể làm:
the-shell < the-script
Hoặc là:
cat the-script | the-shell
Trong trường hợp đó, $0
sẽ là đối số đầu tiên ( argv[0]
) mà trình thông dịch nhận được (ở trên the-shell
, nhưng đó có thể là bất cứ thứ gì mặc dù nói chung là tên cơ sở hoặc một đường dẫn đến trình thông dịch đó).
Phát hiện ra rằng bạn đang ở trong tình huống đó dựa trên giá trị $0
không đáng tin cậy. Bạn có thể nhìn vào đầu ra ps -o args= -p "$$"
để có được manh mối. Trong trường hợp ống, không có cách nào thực sự bạn có thể quay lại đường dẫn đến kịch bản.
Người ta cũng có thể làm:
the-shell -c '. the-script' blah blih
Sau đó, ngoại trừ trong zsh
(và một số triển khai cũ của vỏ Bourne), $0
sẽ là blah
. Một lần nữa, khó có thể đi đến đường dẫn của kịch bản trong các shell đó.
Hoặc là:
the-shell -c "$(cat the-script)" blah blih
Vân vân.
Để đảm bảo bạn có quyền $progname
, bạn có thể tìm kiếm một chuỗi cụ thể trong đó như:
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}:; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
[ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
progname=unknown
Nhưng một lần nữa tôi không nghĩ rằng nó đáng để nỗ lực.
$0
đó ngoài kịch bản, câu trả lời cho tiêu đề câu hỏi. Tuy nhiên, tôi cũng quan tâm đến các tình huống trong đó$0
là chính kịch bản, nhưng không bao gồm thư mục. Cụ thể, tôi đang cố gắng hiểu nhận xét về câu trả lời SO.