【问题标题】:How can a bash script know the directory it is installed in when it is sourced with . operator?bash 脚本在使用 .操作员?
【发布时间】:2010-09-30 02:57:25
【问题描述】:

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

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

问题是 .settings 脚本也需要使用“.”运算符来包含这样的其他文件:

。 .extra_settings

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

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

【问题讨论】:

    标签: linux bash unix shell scripting


    【解决方案1】:

    我们发现$(dirname "$(realpath "$0")") 是最可靠的shbash。由于队友交替使用它们,我们遇到了 sh 不支持的 $BASH_SOURCE 问题。

    相反,我们现在依赖dirname,它也可以堆叠以获得父或祖父文件夹。

    以下示例返回包含 .sh 文件的文件夹的父目录:

    parent_path=$(dirname "$(dirname "$(realpath "$0")")")
    echo $parent_path
    

    【讨论】:

      【解决方案2】:

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

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

      但是,这在获取链接时不起作用。在这种情况下,可能会这样做

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

      注意事项:

      • ${array[x]} 之类的数组不符合 POSIX - 但无论如何,BASH_SOURCE 数组仅在 Bash 中可用
      • 在 macOS 上,原生 BSD readlink 不支持 -f,因此您可能必须使用例如安装 GNU readlinkbrew 替换为brew install coreutils 并将readlink 替换为greadlink
      • 根据您的用例,您可能希望使用-e-m 开关而不是-f 加上可能的-n;有关详细信息,请参阅 readlink 手册页

      【讨论】:

      • 为什么需要[0]? @JasonDay 在他的回答中没有。
      • BASH_SOURCE 是一个表示调用堆栈的数组,第一个元素是当前正在执行的脚本。
      【解决方案3】:

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

      export MYDATE=$(date)
      

      echo "export MYDATE=\$(date)"
      

      也许这是唯一的方法?

      【讨论】:

      • 如果你在 shell 函数中采购,它似乎也会增加复杂性。 (我正在寻找一种适用于 ksh 和 bash 的方法......)
      【解决方案4】:

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

      如果您要获取的文件可能是符号链接,您可以执行以下操作来获取真实目录:

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

      【讨论】:

      • 完美。 $(dirname "$BASH_SOURCE") 正是我想要的。出于我的目的,我不太关心符号链接。感谢所有回复。这里的答案真的很好。
      • 要解析符号链接,您可以这样做:dir=$(dirname $(readlink -f "$BASH_SOURCE"))
      【解决方案5】:

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

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

      然后评估输出:

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

      这就是像modules 这样的包的工作方式。这种方式还可以轻松支持从 sh (X=...; export X) 和 csh (setenv X ...) 派生的 shell

      【讨论】:

      • 对,INN就是这么干的。 “innconfvar”(我想这就是它的名字)以 sh、csh 或 perl 命令的形式发出配置变量。
      • 我喜欢这个主意。但是,我没有使用它,因为副作用会使 .settings 文件更加不可读,因为它已经有很多转义开始。见下文。
      【解决方案6】:

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

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

      【讨论】:

        猜你喜欢
        • 2020-02-27
        • 1970-01-01
        • 2014-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-10
        相关资源
        最近更新 更多