在最常见的情况下,$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
实际上并不包含该脚本的路径。$PATH
the-script
如果可读的话,较新的AT&T ksh
会尝试the-script
在当前目录中进行解释。如果没有它会查找一个可读和可执行 the-script
在$PATH
。
为bash
,它检查是否the-script
是在当前目录(并且不是一个破碎符号链接),并且如果没有,查找为一个可读(不一定可执行文件)the-script
中$PATH
。
zsh
在sh
仿真中,bash
除了the-script
在当前目录中的符号链接损坏之外,它不会搜索the-script
in $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答案的评论。