确定源 shell 脚本的路径

Cas*_*bel 103 shell source

有没有办法让一个来源的shell 脚本找出自己的路径?我主要关注 bash,尽管我有一些使用 tcsh 的同事。

我猜我在这里可能没有很多运气,因为采购导致命令在当前 shell 中执行,所以$0仍然是当前 shell 的调用,而不是源脚本。我目前最好的想法是这样做source $script $script,以便第一个位置参数包含必要的信息。有人有更好的方法吗?

需要明确的是,我正在采购脚本,而不是运行它:

source foo.bash
Run Code Online (Sandbox Code Playgroud)

Den*_*son 74

tcsh,$_脚本的开头将包含该文件的来源位置,如果该文件$0已运行,则该位置将包含该位置。

#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
    echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
    echo "run $0"
endif
Run Code Online (Sandbox Code Playgroud)

在 Bash 中:

#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"
Run Code Online (Sandbox Code Playgroud)

  • @clacke:我发现在我从 2.05b 到 4.2.37 测试的所有 Bash 版本中,包括 4.1.9,`.` 和 `source` 在这方面的工作方式相同。请注意,`$_` 必须在文件的 *first* 语句中访问,否则它将包含上一个命令的最后一个参数。我喜欢将 shebang 包括在内以供我自己参考,这样我就知道它应该用于什么外壳以及用于编辑器,因此它使用语法突出显示。 (2认同)

pbm*_*pbm 36

我认为你可以使用$BASH_SOURCE变量。它返回执行的路径:

pbm@tauri ~ $ /home/pbm/a.sh 
/home/pbm/a.sh
pbm@tauri ~ $ ./a.sh
./a.sh
pbm@tauri ~ $ source /home/pbm/a.sh 
/home/pbm/a.sh
pbm@tauri ~ $ source ./a.sh
./a.sh
Run Code Online (Sandbox Code Playgroud)

所以下一步我们应该检查路径是否是相对的。如果不是相对的,一切都好。如果是,我们可以用 来检查路径pwd,用/和连接$BASH_SOURCE

  • 请注意,如果给定的名称不包含 `/`,则 `source` 在 `$PATH` 中搜索。搜索顺序取决于 shell 选项,详细信息请参阅手册。 (3认同)

gkb*_*986 23

此解决方案仅适用于 bash 而不是 tcsh。请注意,${BASH_SOURCE[0]}如果您尝试从函数中查找路径,通常提供的答案将不起作用。

我发现这一行始终有效,无论文件是源文件还是作为脚本运行。

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
Run Code Online (Sandbox Code Playgroud)

如果你想readlink在你上面得到的路径上使用符号链接,递归或非递归。

双引号可防止路径中的空格将结果拆分为数组,并且不仅用于echo,而且在任何上下文中都需要使用双引号。由于echo在数组项之间插入一个空格,除非路径中有两个或多个连续空格,否则差异将被隐藏。

这是一个用于尝试并将其与其他建议的解决方案进行比较的脚本。将其调用为source test1/test2/test_script.shor bash test1/test2/test_script.sh

echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
Run Code Online (Sandbox Code Playgroud)
#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo "${BASH_SOURCE}"
echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"

cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"

function test_within_func_inside {
    echo "${BASH_SOURCE}"
    echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
}

echo "Testing within function inside"
test_within_func_inside

echo "Testing within function outside"
test_within_func_outside
Run Code Online (Sandbox Code Playgroud)

one-liner 工作的原因可以通过使用BASH_SOURCE环境变量及其关联来解释FUNCNAME

BASH_SOURCE

一个数组变量,其成员是源文件名,其中定义了 FUNCNAME 数组变量中相应的 shell 函数名。shell 函数 ${FUNCNAME[$i]} 在文件 ${BASH_SOURCE[$i]} 中定义并从 ${BASH_SOURCE[$i+1]} 调用。

函数名

一个数组变量,包含当前在执行调用堆栈中的所有 shell 函数的名称。索引为 0 的元素是任何当前正在执行的 shell 函数的名称。最底部的元素(索引最高的元素)是“main”。此变量仅在执行 shell 函数时存在。对 FUNCNAME 的赋值无效并返回错误状态。如果 FUNCNAME 未设置,它会失去其特殊属性,即使它随后被重置。

此变量可与 BASH_LINENO 和 BASH_SOURCE 一起使用。FUNCNAME 的每个元素在 BASH_LINENO 和 BASH_SOURCE 中都有对应的元素来描述调用栈。例如,${FUNCNAME[$i]} 是从文件 ${BASH_SOURCE[$i+1]} 行号 ${BASH_LINENO[$i]} 处调用的。内置调用者使用此信息显示当前调用堆栈。

[来源:Bash 手册]


Sha*_*off 22

为了彻底和搜索者着想,这里是它们的作用......它是一个社区维基,所以可以随意添加其他 shell 的等效项(显然, $BASH_SOURCE 会有所不同)。

测试.sh:

#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE
Run Code Online (Sandbox Code Playgroud)

测试2.sh:

#! /bin/sh
source ./test.sh
Run Code Online (Sandbox Code Playgroud)

重击:

$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh
Run Code Online (Sandbox Code Playgroud)

短跑

$./test2.sh
./test2.sh
./test2.sh
./test2.sh

$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh

$
Run Code Online (Sandbox Code Playgroud)

Zsh

$ ./test2.sh
./test.sh
./test.sh
./test.sh

$ zsh test.sh

echo
test.sh

$
Run Code Online (Sandbox Code Playgroud)

  • @JamesThomasMoon1979:你在说什么?在 shell 脚本中任何以 `#` 开头的都是注释。``#!`` (shebang) 仅作为 * 执行的脚本的第一行具有其特殊含义。* 作为 * 来源的文件的第一行,* 它只是一个注释。 (6认同)
  • @CiroSantilli:并非总是如此,请阅读有关 `$_` 特殊参数的 [Bash 手册](http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters):“在 shell 启动时, 设置为用于调用在环境或参数列表中传递的正在执行的 shell 或 shell 脚本的绝对路径名。随后,扩展到上一个命令的最后一个参数,扩展后。还设置为用于调用每个的完整路径名命令执行并放置在导出到该命令的环境中。检查邮件时,此参数保存邮件文件的名称。” (5认同)

小智 18

这在 bash、dash、ksh 和 zsh 中对我有用:

if test -n "$BASH" ; then script=$BASH_SOURCE
elif test -n "$TMOUT"; then script=${.sh.file}
elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
else script=$0
fi

echo $script
Run Code Online (Sandbox Code Playgroud)

这些外壳的输出:

BASH source: ./myscript
ZSH source: ./myscript
KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
BASH: ./myscript
ZSH: ./myscript
KSH: /home/pbrannan/git/theme/src/theme/web/myscript
DASH: ./myscript
Run Code Online (Sandbox Code Playgroud)

我试图让它适用于 csh/tcsh,但是太难了;我坚持使用 POSIX。