这是某业务广泛的 QQ 群里的一次讨论,尘光大前辈问起都是怎么获取脚本所在绝对路径。
首先有同学表示可以使用
pwd
配合
$0
参数,但是很明显脚本执行的时候并不一定在当前 working dir,这就会带来一个明显的问题就是,如果执行的方式是
./a.sh
,那么获取到的$0和 pwd 的结果才是相同的,否则就是不同的:
当然,可以说判断执行的方式,根据判断$0是否是绝对路径等方法可以区分这个情况,但是首先这样就变得麻烦,其次软链接的情况就不能通过这个判断,一个简单的验证:
尘光前辈关于软链接的实际应用的陈述:
~/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 递归功能。
依旧是一个简单的验证:
至于尘光前辈提到的 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 提供了很多神奇的思路。
本文链接:https://www.starduster.me/2016/10/24/how-shell-script-judge-its-working-dir/
本站基于 Creactive Commons BY-NC-SA 4.0 License 允许并欢迎您在注明来源和非商业使用前提下自由地对本文进行复制、分享或基于本文进行创作。
请注意:受限于笔者水平,本站内容可能存在主观臆断或事实错误,文中信息也可能因时间推移而不再准确,在此提醒读者结合自身判断谨慎地采纳。