获取 Shell 脚本所在目录的绝对路径

写脚本时经常需要引用脚本同目录下的其他文件(配置、日志目录、依赖脚本等)。这时很多人的第一反应是用 pwd 或者 $0,但这两种做法都有陷阱。总结一下,最可靠的做法其实就是一条命令:

root_path=$(cd $(dirname ${BASH_SOURCE[0]}); pwd)

为什么不能用 pwd

pwd 返回的是当前工作目录,也就是你执行脚本时所在的目录,而不是脚本文件所在的目录。比如:

$ pwd
/home/user/project
$ /opt/tools/myapp/run.sh

此时脚本内部执行 pwd 得到的是 /home/user/project,而脚本实际位于 /opt/tools/myapp/。如果脚本里用 pwd 拼路径去读配置,只要换一个目录启动脚本就会出错。

为什么不能直接用 $0

$0 是脚本被调用时的名字,它有几个问题:

  1. 如果用相对路径执行(如 cd /opt/tools; ./myapp/run.sh),$0 只是 ./myapp/run.sh 这样的相对路径,不是绝对路径;
  2. 如果脚本被 source 进来,$0 保留的是外层 Shell 的名字(比如 -bash),完全不是脚本自己的路径;
  3. 在被其他脚本调用等场景下,$0 的值也不可靠。

BASH_SOURCE[0] 则始终指向”当前正在执行的这段代码所在的脚本文件”,即使脚本被 source,也能拿到正确的路径,因此比 $0 更稳。

命令逐部分拆解

root_path=$(cd $(dirname ${BASH_SOURCE[0]}); pwd)
  • ${BASH_SOURCE[0]}:当前脚本自身的文件名(相对于调用时的路径,可能是绝对路径、相对路径或裸文件名)。
  • $(dirname ...):取路径中的目录部分。比如 ${BASH_SOURCE[0]}./tools/run.shdirname 之后得到 ./tools;如果只是 run.sh,则得到 .
  • cd ... ; pwd:先 cd 进这个目录(无论相对路径是什么形状,cd 之后 pwd 一定返回规范的绝对路径),再输出当前绝对路径。
  • $( )(最外层):命令替换,把子 Shell 中 pwd 的输出赋值给变量 root_path

整个过程发生在一个子 Shell 中,所以中途的 cd 不会影响脚本当前的工作目录,这也是这条命令写法上的精妙之处。

使用示例

#!/bin/bash

root_path=$(cd $(dirname ${BASH_SOURCE[0]}); pwd)
echo "脚本所在目录:$root_path"
# 加载同目录下的配置
source "$root_path/config.env"

无论从哪个目录、用绝对路径还是相对路径执行这个脚本,root_path 都会正确指向脚本文件所在的目录。

小结

  • pwd 是”人在哪里”,不是”脚本在哪里”;
  • $0 取决于调用方式,source 时会失真;
  • $(cd $(dirname ${BASH_SOURCE[0]}); pwd) 通过子 Shell 先跳转再取址,稳定可靠,可以直接背下来当模板用。

   转载规则


《获取 Shell 脚本所在目录的绝对路径》 吴杭沉 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Shell 分割字符串的常用方法 Shell 分割字符串的常用方法
处理日志、配置文件或命令输出时,按分隔符把一个字符串切成几段是最常见的操作之一。Shell 下能做这件事的工具不少:cut、awk、IFS、参数展开、tr 各有适用场景,本文逐一给出示例。 以下示例统一使用这个字符串: str="192.1
2020-11-22
下一篇 
history:让历史命令显示执行时间 history:让历史命令显示执行时间
Linux 下用 history 查看历史命令时,默认只显示序号和命令本身,看不到这条命令是什么时候执行的,排查问题时很不方便。其实只要设置 HISTTIMEFORMAT 环境变量,就能让 history 显示每条命令的执行时间。 临时生效
2020-11-08
  目录