在Bash脚本中获取脚本所在目录的方法

在Bash脚本中获取脚本所在目录的方法

技术背景

在编写Bash脚本时,有时需要获取脚本本身所在的目录,以便于访问同一目录下的其他文件或资源。然而,由于脚本的调用方式多样(如相对路径、绝对路径、符号链接等),获取脚本所在目录并非总是一件简单的事情。

实现步骤

简单单行程式

1
2
#!/usr/bin/env bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" > /dev/null && pwd )

此单行程式可以获取脚本的完整目录名,无论脚本从何处被调用。但要求用于查找脚本的路径的最后一个组件不是符号链接(目录链接是可以的)。

解决符号链接问题的多行解决方案

1
2
3
4
5
6
7
8
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
SOURCE=$(readlink "$SOURCE")
[[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )

此方案可以处理别名、sourcebash -c、符号链接等各种情况。

详细输出的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
TARGET=$(readlink "$SOURCE")
if [[ $TARGET == /* ]]; then
echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
SOURCE=$TARGET
else
DIR=$( dirname "$SOURCE" )
echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
SOURCE=$DIR/$TARGET # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
fi
done
echo "SOURCE is '$SOURCE'"
RDIR=$( dirname "$SOURCE" )
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
if [ "$DIR" != "$RDIR" ]; then
echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

运行此脚本可以详细看到解析符号链接的过程。

1
dirname -- "$( readlink -f -- "$0"; )"

readlink会将脚本路径解析为从文件系统根目录开始的绝对路径,任何包含单点、双点、波浪号和/或符号链接的路径都会被解析为完整路径。

完整示例脚本

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env bash
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[0]}")"
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME_STEM="${SCRIPT_FILENAME%.*}" # withOUT the extension
SCRIPT_FILENAME_EXTENSION="${SCRIPT_FILENAME##*.}" # JUST the extension
echo "FULL_PATH_TO_SCRIPT: $FULL_PATH_TO_SCRIPT"
echo "SCRIPT_DIRECTORY: $SCRIPT_DIRECTORY"
echo "SCRIPT_FILENAME: $SCRIPT_FILENAME"
echo "SCRIPT_FILENAME_STEM: $SCRIPT_FILENAME_STEM"
echo "SCRIPT_FILENAME_EXTENSION: $SCRIPT_FILENAME_EXTENSION"

此脚本可以获取脚本的完整路径、所在目录、文件名、文件名(不带扩展名)和文件扩展名。

核心代码

以下是一个获取脚本所在目录的完整示例:

1
2
3
4
#!/usr/bin/env bash
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[0]}")"
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
echo "SCRIPT_DIRECTORY: $SCRIPT_DIRECTORY"

最佳实践

  • 在脚本开头获取脚本所在目录,避免在脚本执行过程中因cd命令改变当前工作目录而导致获取的目录不准确。
  • 对于需要处理符号链接的情况,使用多行解决方案或结合readlink命令。
  • 为了提高脚本的可移植性,尽量使用POSIX兼容的命令和语法。

常见问题

  • cd到不同目录后运行代码结果可能不正确:在运行获取脚本所在目录的代码之前,如果使用cd命令切换到了其他目录,可能会导致结果不准确。因此,建议在脚本开头获取脚本所在目录。
  • $CDPATH问题$CDPATH环境变量可能会影响cd命令的行为,导致获取的目录不准确。可以在cd命令后面添加>/dev/null 2>&1来避免这个问题。
  • 脚本被source时的问题:当脚本被source时,$0可能会指向bash而不是脚本本身。可以使用${BASH_SOURCE[0]}来解决这个问题。

在Bash脚本中获取脚本所在目录的方法
https://119291.xyz/posts/2025-05-07.methods-to-get-script-directory-in-bash/
作者
ww
发布于
2025年5月7日
许可协议