当 bash 脚本以 . 操作员?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/421772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 20:37:01  来源:igfitidea点击:

How can a bash script know the directory it is installed in when it is sourced with . operator?

linuxbashunixshellscripting

提问by Gary

What I'd like to do is to include settings from a file into my current interactive bash shell like this:

我想要做的是将文件中的设置包含到我当前的交互式 bash shell 中,如下所示:

$ . /path/to/some/dir/.settings

$ . /path/to/some/dir/.settings

The problem is that the .settings script also needs to use the "." operator to include other files like this:

问题是 .settings 脚本也需要使用“.”。运算符以包含其他文件,如下所示:

. .extra_settings

. .extra_settings

How do I reference the relative path for .extra_settings in the .settings file? These two files are always stored in the same directory, but the path to this directory will be different depending on where these files were installed.

如何在 .settings 文件中引用 .extra_settings 的相对路径?这两个文件总是存储在同一个目录中,但是根据这些文件的安装位置,该目录的路径会有所不同。

The operator always knows the /path/to/some/dir/ as shown above. How can the .settings file know the directory where it is installed? I would rather not have an install process that records the name of the installed directory.

操作员总是知道 /path/to/some/dir/ ,如上所示。.settings 文件如何知道它的安装目录?我宁愿没有记录安装目录名称的安装过程。

回答by Jason Day

I believe $(dirname "$BASH_SOURCE")will do what you want, as long as the file you are sourcing is nota symlink.

我相信$(dirname "$BASH_SOURCE")会做你想做的,只要你采购的文件不是符号链接。

If the file you are sourcing may be a symlink, you can do something like the following to get the true directory:

如果您正在采购的文件可能是一个符号链接,您可以执行以下操作来获取真正的目录:

PRG="$BASH_SOURCE"
progname=`basename "$BASH_SOURCE"`

while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done

dir=$(dirname "$PRG")

回答by Jacob Lee

Here is what might be an elegant solution:

这可能是一个优雅的解决方案:

script_path="${BASH_SOURCE[0]}"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"

This will not, however, work when sourcing links. In that case, one might do

但是,这在采购链接时不起作用。在那种情况下,人们可能会这样做

script_path="$(readlink -f "$(readlink "${BASH_SOURCE[0]}")")"
script_dir="$(cd "$(dirname "${script_path}")" && pwd)"

Things to note:

注意事项:

  • arrays like ${array[x]}are not POSIX compliant - but then, the BASH_SOURCEarray is only available in Bash, anyway
  • on macOS, the native BSD readlinkdoes not support -f, so you might have to install GNU readlinkusing e.g. brewby brew install coreutilsand replace readlinkby greadlink
  • depending on your use case, you might want to use the -eor -mswitches instead of -fplus possibly -n; see readlinkman page for details
  • 像这样${array[x]}的数组不符合 POSIX - 但是BASH_SOURCE,无论如何,该数组只能在 Bash 中使用
  • 在 macOS 上,本机 BSDreadlink不支持-f,因此您可能必须readlink使用例如brewbybrew install coreutils和 replace readlinkby安装 GNUgreadlink
  • 根据您的用例,您可能希望使用-e-m开关而不是-fplus 可能-n;有关详细信息,请参阅readlink手册页

回答by orip

A different take on the problem - if you're using "." in order to set environment variables, another standard way to do this is to have your script echo variable setting commands, e.g.:

对问题的不同看法 - 如果您使用“。” 为了设置环境变量,另一种标准方法是让您的脚本回显变量设置命令,例如:

# settings.sh
echo export CLASSPATH=${CLASSPATH}:/foo/bar

then eval the output:

然后评估输出:

eval $(/path/to/settings.sh)

That's how packages like moduleswork. This way also makes it easy to support shells derived from sh (X=...; export X) and csh (setenv X ...)

这就是像模块这样的包的工作方式。这种方式也可以很容易地支持从 sh( X=...; export X) 和 csh( setenv X ...)派生的 shell

回答by Gary

I tried messing with variants of $(dirname $0) but it fails when the .settings file is included with ".". If I were executing the .settings file instead of including it, this solution would work. Instead, the $(dirname $0) always returns ".", meaning current directory. This fails when doing something like this:

我尝试弄乱 $(dirname $0) 的变体,但是当 .settings 文件包含在“.”中时它失败了。如果我正在执行 .settings 文件而不是包含它,则此解决方案将起作用。相反,$(dirname $0) 总是返回“.”,表示当前目录。执行以下操作时会失败:

$ cd / $ . /some/path/.settings

$ cd / $。/some/path/.settings

回答by Gary

This sort of works. It works in the sense that you can use the $(dirname $0) syntax within the .settings file to determine its home since you are executing this script in a new shell. However, it adds an extra layer of convolution where you need to change lines such as:

这样的作品。它的工作原理是,您可以在 .settings 文件中使用 $(dirname $0) 语法来确定其主页,因为您是在新 shell 中执行此脚本。但是,它增加了一个额外的卷积层,您需要在其中更改行,例如:

export MYDATE=$(date)

to

echo "export MYDATE=$(date)"

Maybe this is the only way?

也许这是唯一的方法?