Shell语言用例

bash脚本里如何得到脚本文件所在路径

2018-11-21  本文已影响0人  CodingCode

bash脚本里如何得到脚本文件所在路径

应用背景

我们在脚本里面会经常调用外部程序,如何指定外部程序路径:

  1. 绝对路径写死;缺点是不方便移植,比如更改安装路径等。
  2. 加到PATH搜索目录下面;缺点是PATH路径会被替换,同名程序的存在会导致调用到另一个程序。

而有时,我们希望被调用的外部程序是相对当前脚本文件的,例如和脚本文件在同一目录下面,或者临近相对的子目录等等;

|-- ROOT
|-- mains.h
|   |-- scripts
|   |   |-- sub1.sh
|   |   |-- sub2.sh
|   |-- tools
|   |   |-- tool1
|   |   |-- tool2

例如main.sh是主脚本,它需要调用scripts/sub.sh,tools/tool1,和tools/tool2,此时如果把所有的路径(ROOT:ROOT/scripts:ROOT/tools)都加到PATH里面显得有点重,那么我们只想把ROOT加到PATH里面,然后让main.sh来去搜索sub.sh和tool。

思路

这种场景下,我们就需要在main.sh里面得到脚本自己所在的路径(例如,SCRIPTDIR),然后按如下方式调用子程序:

${SCRIPTDIR}/scripts/sub.sh
${SCRIPTDIR}/tools/tool

方法

如何得到main.sh脚本所在的路径呢,下述命令可以得到:

SCRIPTDIR=$(cd $(dirname "${BASH_SOURCE[0]}") >/dev/null && pwd)
  1. 例子1:

假设脚本main.sh所在的路径是/home/<username>/main.sh

$ cat main.sh 
#!/bin/bash

SCRIPTDIR=$(cd $(dirname "${BASH_SOURCE[0]}") >/dev/null && pwd)

echo ${SCRIPTDIR}
$ pwd
/home/<username>/tmp
$ ./main.sh 
/home/<username>/tmp
$ cd ..
$ pwd
/home/<username>
$ ./tmp/main.sh 
/home/<username>/tmp

我们可以看到,不管当前的路径在哪里,调用起main.sh的时候它都能打印出脚本所在的路径。

再看一个例子。

  1. 例子2:
$ pwd
/home/<username>
$ ln -snf tmp/main.sh main.sh
$ ./main.sh 
/home/<username>

我们创建一个符号链接,main.sh链接到tmp/main.sh,然后我们运行外层的main.sh这个符号链接,结果我们看到:打印出来的是符号链接文件所在的路径,而不是脚本所在的路径。

这是合理的设计行为,因为我们调用的是外面的main.sh这个链接文件,但是对我们的需求就有问题了,因为在main.sh这个真正脚本里面就没法调用到子命令sub.sh和tool了,无法根据外面符号链接的路径来找scripts和tools路径了。

解决办法呢,我们要不停的解决符号链接行为,直到遇到真正的脚本为止。
重新改写tmp/main.sh脚本如下:

#!/bin/bash

#SCRIPTDIR=$(cd $(dirname "${BASH_SOURCE[0]}") >/dev/null && pwd)
SOURCE="${BASH_SOURCE[0]}"
while [ -h "${SOURCE}" ]; do
  SCRIPTDIR="$(cd -P "$(dirname "${SOURCE}")" >/dev/null && pwd)"
  SOURCE="$(readlink "${SOURCE}")"
  [[ ${SOURCE} != /* ]] && SOURCE="${SCRIPTDIR}/${SOURCE}"
done
SCRIPTDIR="$(cd -P "$(dirname "${SOURCE}")" >/dev/null && pwd)"

echo ${SCRIPTDIR}

再次运行符号链接文件:

$ pwd
/home/<username>
$ ln -snf tmp/main.sh main.sh
$ ./main.sh 
/home/<username>/tmp

这回打印出来的就是真正的脚本文件所在的路径了。

上一篇 下一篇

猜你喜欢

热点阅读