-1

这个问题与:Getting the source directory of a Bash script from inside - 在那个问题中,有一个完美的答案显示 show 以获取当前运行的 bash 脚本的目录(包括读取符号链接的实际位置)。

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 )"

这完美地工作,除了它是很多样板代码。我正在开发一个脚本包,其中包含许多相互使用的小脚本。此脚本包托管在 git repo 中,它必须与位置无关。例如,无论它被克隆到哪个目录,它都应该始终以相同的方式工作。必须能够将此 repo 克隆到许多不同的目录中并独立使用它们。许多脚本只是命令的包装器,或者它们正在调用同一目录中的其他脚本,或者相对于包含目录。例如,这是一个模拟 'mongo' 命令的命令,但在 docker 容器中运行它:

#!/bin/bash

set -e

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 )"
# mongo-container is another script that determines the container id
# and it must be referenced relatively to the containing directory
MONGOE="docker exec -ti `${DIR}/mongo-container`"
${MONGOE} mongo "$@"

这段代码的 90% 用于确定脚本的目录,只有 10% 做了一些有用的事情。我有大约 20 个这样的脚本。这意味着我 90% 的 bash 脚本只是“确定包含目录”。当我看着它们时,我几乎看不到它们在做什么,因为有太多的样板代码。一定有更好的方法,我就是想不通。

有任何想法吗?

4

3 回答 3

2

也许您想在专用的通用 GNU/Bash 脚本中“分解”此源代码?

首先,我认为您可以使用大多数 GNU 操作系统上可用的which命令(通常无需额外安装)

https://savannah.gnu.org/projects/which/

然后,您可以创建一个“通用功能”文件,其中包含用于定义完整路径的源代码,包括符号链接管理,我们称之为/tmp/myTrueDir/common.sh

#!/bin/bash

set -e

# Usage: resolveCompleteAbsolutePath <$0 path to regard>
function resolveCompleteAbsolutePath() {
  local SOURCE="$1"
  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 )"

  echo "$DIR"
}

然后您可以在所有小脚本的开头使用它(首先我们不必管理符号链接以访问位于脚本同一目录中的 common.sh 文件)

currentDir=$( dirname "$( which "$0" )" )
source "$currentDir/common.sh"

最后,您可以使用resolveCompleteAbsolutePath函数(在您的common.sh文件中定义)来获取完整路径,包括符号链接解析。

这样,您的脚本将只包含您想要的有趣源代码,并且路径管理将在同一个地方分解。

例如,您可以使用以下文件系统结构轻松测试所有这些:

/tmp/myTrueDir/
/tmp/myTrueDir/common.sh
/tmp/myTrueDir/test.sh

使用具有以下示例行的/tmp/myTrueDir/test.sh文件:

#!/bin/bash

currentDir=$( dirname "$( which "$0" )" )
source "$currentDir/common.sh"
resolvedPath=$( resolveCompleteAbsolutePath "$0" )

echo "currentDir: $currentDir"
echo "resolvedPath: $resolvedPath"

然后你可以创建一个指向你的真实目录的符号链接,比如说:

ln -s /tmp/myTrueDir /tmp/mySymbDir

然后您可以从符号路径(即 /tmp/mySymbDir/test.sh)调用您的测试脚本,并查看它是否像您想要的那样工作:

currentDir: /tmp/mySymbDir
resolvedDir: /tmp/myTrueDir
于 2018-10-22T10:06:34.807 回答
2
于 2018-10-22T18:44:11.840 回答
1

我倾向于将以下行放在我的代码中

PROGDIR=$(cd $(dirname $0) && pwd)

这将更改为脚本的目录,然后运行 ​​pwd 以获取目录路径。我从来没有遇到过这个问题。

希望这可以帮助。

于 2018-10-22T12:00:34.817 回答