shell 脚本如何判断所在目录问题

这是某业务广泛的 QQ 群里的一次讨论,尘光大前辈问起都是怎么获取脚本所在绝对路径。

首先有同学表示可以使用 pwd配合 $0参数,但是很明显脚本执行的时候并不一定在当前 working dir,这就会带来一个明显的问题就是,如果执行的方式是 ./a.sh,那么获取到的$0和 pwd 的结果才是相同的,否则就是不同的:

qq20161024-02x

当然,可以说判断执行的方式,根据判断$0是否是绝对路径等方法可以区分这个情况,但是首先这样就变得麻烦,其次软链接的情况就不能通过这个判断,一个简单的验证:

qq20161024-12x

尘光前辈关于软链接的实际应用的陈述:

~/bin/setup-firewall.sh -> /opt/gateway/bin/setup-firewall.sh 这种情况,如果是 ~/bin/setup-firewall.sh 这样运行的话,pwd的方案获取到的是 ~/bin/setup-firewall.sh,而不是 /opt/gateway,但是我显然不关心 ~/bin/ 这个目录,得到一个软链没有实际作用。

比如可能是这样的:
/usr/bin/gcc -> /etc/alternatives/gcc -> /usr/bin/gcc-xxx
或者更远的:
/usr/bin/editor -> /etc/alternatives/editor -> /usr/bin/some-editor -> /usr/bin/some-editor-$version

所以最简洁的就是 readlink -f $(which $0) ,readlink -f 可以递归的跟软链。

实际上像偷懒也不是不行,先 cd 可以简单的解决这个问题,但是如果目的是为了在各个地方都能直接运行脚本,综合来看简洁优雅的办法是 __script_base=$(dirname $(readlink -f $(which $0))) 然而前辈在 Mac 上第一次跑就挂掉了,因为 Mac 的 readlink 没有 -f 递归功能

依旧是一个简单的验证:

qq20161024-22x

至于尘光前辈提到的 Mac 上 readlink没有 -f 参数,实际上 homebrew 里提供了 GNU coreutils,为避免和 BSD 风格的命令产生冲突,安装后的 coreutils 命令全部需要加 g 前缀, brew install coreutils后使用 greadlink或者 grealpath即可;如果需要使用 GNU 风格的命令直接替代系统自带命令,根据 homebrew 的提示,自行添加 PATH 即可: export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"

关于这个问题,也可以参见阅读 http://stackoverflow.com/questions/4774054/reliable-way-for-a-bash-script-to-get-the-full-path-to-itself 提供了很多神奇的思路。

发表评论

电子邮件地址不会被公开。 必填项已用*标注