在最常见的情况下,$0将包含相对于脚本的绝对路径或相对路径,因此
script_path=$(readlink -e -- "$0")
(假设有一个readlink命令并且它支持-e)通常是一种获得脚本的规范绝对路径的好方法。
$0 从指定传递给解释器的脚本的参数分配。
例如,在:
the-shell -shell-options the/script its args
$0得到the/script。
运行时:
the/script its args
您的shell将执行以下操作:
exec("the/script", ["the/script", "its", "args"])
#! /bin/sh -例如,如果脚本中包含“ she-bang”,系统会将其转换为:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(如果它不包含she-bang,或者更一般而言,如果系统返回ENOEXEC错误,则它是您的shell,它将执行相同的操作)
在某些系统上,setuid / setgid脚本有一个例外,其中系统将在某些系统上打开脚本fd x并运行:
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
以避免比赛条件(在这种情况下$0将包含/dev/fd/x)。
现在,您可能会争辩说这/dev/fd/x 是该脚本的路径。但是请注意,如果您从阅读$0,则会在使用输入时中断脚本。
现在,如果所调用的脚本命令名称不包含斜杠,则存在区别。在:
the-script its args
你的shell将查找the-script在$PATH。$PATH可能包含某些目录的绝对或相对(包括空字符串)路径。例如,如果在当前目录中$PATH包含/bin:/usr/bin:和the-script,则外壳程序将执行以下操作:
exec("the-script", ["the-script", "its", "args"])
它将变成:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
或者在/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")
在上述所有情况下,除了setuid角落情况外,$0都将包含脚本的路径(绝对路径或相对路径)。
现在,脚本也可以称为:
the-interpreter the-script its args
当the-script上面不包含斜杠字符时,该行为因外壳而异。
以前的AT&T ksh实现实际上是无条件地在$PATH其中查找脚本(这实际上是bug和setuid脚本的安全漏洞),因此,除非查找实际上是在当前目录中找到的,否则$0实际上并不包含该脚本的路径。$PATHthe-script
如果可读的话,较新的AT&T ksh会尝试the-script在当前目录中进行解释。如果没有它会查找一个可读和可执行 the-script在$PATH。
为bash,它检查是否the-script是在当前目录(并且不是一个破碎符号链接),并且如果没有,查找为一个可读(不一定可执行文件)the-script中$PATH。
zsh在sh仿真中,bash除了the-script在当前目录中的符号链接损坏之外,它不会搜索the-scriptin $PATH而是会报告错误。
所有其他类似Bourne外壳看起来不the-script起来$PATH。
无论如何,对于所有这些shell,如果您发现其中$0不包含/且不可读,则可能已在中进行了查找$PATH。然后,由于其中的文件$PATH可能是可执行文件,因此使用它command -v -- "$0"来查找路径可能是一种安全的近似方法(尽管如果$0碰巧也是shell内置名称或关键字的名称(在大多数shell中),则该方法将无效)。
因此,如果您真的想涵盖这种情况,可以编写:
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
(""附加到$PATH,以保留带有外壳的尾随空元素,这些外壳$IFS充当分隔符而不是分隔符)。
现在,有更多深奥的方法可以调用脚本。一个可以做:
the-shell < the-script
要么:
cat the-script | the-shell
在这种情况下,$0它将是解释器收到的第一个参数(argv[0])(在之上,但可以是任何东西,尽管通常是该解释器的基本名称或一个路径)。the-shell
根据的值检测您处于那种情况$0是不可靠的。您可以查看的输出ps -o args= -p "$$"以获取线索。在管道情况下,没有真正的方法可以返回脚本路径。
一个人也可以做:
the-shell -c '. the-script' blah blih
然后,除了中的内容zsh(以及Bourne shell的某些旧实现)之外,$0将为blah。同样,在这些shell中很难到达脚本的路径。
要么:
the-shell -c "$(cat the-script)" blah blih
等等
为了确保您有权利$progname,您可以在其中搜索特定的字符串,例如:
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
但是我再次认为这样做是不值得的。
$0脚本以外的其他问题的情况,脚本确实回答了问题标题。但是,我也对$0脚本本身但不包含目录的情况感兴趣。特别是,我试图理解对SO答案的评论。