协慌网

登录 贡献 社区

从内部获取 Bash 脚本的源目录

如何获取其中的目录路径的 Bash脚本所在,该脚本里面

例如,假设我想使用 Bash 脚本作为另一个应用程序的启动器。我想将工作目录更改为 Bash 脚本所在的目录,因此我可以对该目录中的文件进行操作,如下所示:

$ ./application

答案

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

是一个有用的单行程序,它将为您提供脚本的完整目录名称,无论它在何处被调用。

只要用于查找脚本的路径的最后一个组件不是符号链接(目录链接正常),它就会起作用。如果您还想解决脚本本身的任何链接,您需要一个多线解决方案:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null && 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 && pwd )"

最后一个将适用于别名, sourcebash -c ,符号链接等的任意组合。

注意:如果在运行此代码段之前cd到其他目录,结果可能不正确!另外,请注意$CDPATH陷阱

要了解它是如何工作的,请尝试运行这个更详细的表单:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$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" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

它将打印如下:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

使用dirname "$0"

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

如果您没有从包含它的目录运行脚本,则单独使用pwd将不起作用。

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

dirname 命令是最基本的,只需解析直到 $ 0(脚本名称)变量的文件名的路径:

dirname "$0"

但是,正如matt b指出的那样,返回的路径根据脚本的调用方式而有所不同。 pwd 不能完成这项工作,因为它只告诉你当前目录是什么,而不是脚本所在的目录。另外,如果执行了一个脚本的符号链接,你将获得一个(可能是相对的)路径到链接所在的位置,而不是实际的脚本。

其他一些人提到了readlink命令,但最简单的是,您可以使用:

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

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

这是一个演示这些内容的脚本, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

使用相对路径在我的主目录中运行此脚本:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

同样,但使用脚本的完整路径:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

现在更改目录:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

最后使用符号链接来执行脚本:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat