Trong các trường hợp phổ biến nhất, $0sẽ 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 readlinklệ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 xvà 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 $0nà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-scripttrong $PATH. $PATHcó 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 $PATHchứ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, $0sẽ 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-scriptnhư 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 kshtriể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 $0thực sự không có đường dẫn đến tập lệnh trừ khi việc $PATHtìm kiếm thực sự tìm thấy the-scripttrong thư mục hiện tại.
AT & T mới hơn kshsẽ thử và diễn giải the-scripttrong 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-scriptlà 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-scripttrong $PATH.
zshtrong shthi đua sẽ làm như bashtrừ rằng nếu the-scriptlà 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-scripttrong $PATHvà 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-scriptlên $PATH.
Đối với tất cả các shell đó, nếu bạn thấy rằng $0nó 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 $PATHcó 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 $0cũ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 $PATHlà để bảo toàn phần tử rỗng có dấu với các lớp vỏ có $IFSvai 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 đó, $0sẽ 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ị $0khô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), $0sẽ 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 đó$0là 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.