查找源 shell 脚本的位置

Mar*_*rco 8 shell path

来源的 shell 脚本是否有可能知道它的位置?我读过确定来源的shell脚本路径,但答案集中在bashtcsh,如果使用POSIX外壳失败。$0也不是解决方案并产生错误的结果

解决方案不需要 100% 可靠。路径不太可能包含硬链接或符号链接。

# sourcing the script should yield the absolute path to the script
. somedir/thescript

# within “thescript”
-> /tmp/foo/bar/somedir
Run Code Online (Sandbox Code Playgroud)

一些背景:该脚本是现有应用程序的一部分,其中包含bin与源脚本相关的已知位置(因架构而异)的目录中的数十个二进制文件。要使用该应用程序,用户需要将bin目录PATH放在 .

Gil*_*il' 10

除非您使用提供 POSIX 规范扩展的 shell,否则源脚本的位置不可用。您可以使用以下代码段对此进行测试:

env -i PATH=/usr/bin:/bin sh -c '. ./included.sh' | grep included
Run Code Online (Sandbox Code Playgroud)

其中included.sh包含

echo "$0"
set
Run Code Online (Sandbox Code Playgroud)

在 bash 中,源脚本的名称在$BASH_SOURCE. 在 zsh 中(在 zsh 兼容模式下,而不是在 sh 或 ksh 兼容模式下),它在$0(注意,在函数中,$0是函数名)。在 pdksh 和 dash 中,它不可用。在 ksh93 中,此方法不会显示解决方案,但包含脚本的完整路径可作为${.sh.file}.

如果需要 bash 或 ksh93 或 zsh 就足够了,您可以使用以下代码段:

if [ -n "$BASH_SOURCE" ]; then
  this_script=$BASH_SOURCE
elif [ -n "$ZSH_VERSION" ]; then
  setopt function_argzero
  this_script=$0
elif eval '[[ -n ${.sh.file} ]]' 2>/dev/null; then
  eval 'this_script=${.sh.file}'
else
  echo 1>&2 "Unsupported shell. Please use bash, ksh93 or zsh."
  exit 2
fi
Run Code Online (Sandbox Code Playgroud)

您可以通过查看 shell 打开的文件来尝试猜测脚本的位置。实验上,这似乎适用于 dash 和 pdksh,但不适用于 bash 或 ksh93,至少对于短脚本而言,它们在执行脚本文件时已关闭脚本文件。

  open_file=$(lsof -F n -p $$ | sed -n '$s/^n//p')
  if [ -n "$open_file" ]; then
    # best guess: $open_file is this script
  fi
Run Code Online (Sandbox Code Playgroud)

如果脚本源自一个复杂的脚本,该脚本一直在玩重定向,则该脚本可能不是具有最高编号描述符的文件。您可能希望遍历打开的文件。无论如何,这不能保证工作。定位源脚本的唯一可靠方法是使用 bash、ksh93 或 zsh。


如果您可以更改接口,那么不要获取您的脚本,而是让您的脚本打印出要传递给eval调用者的 shell 片段。这是设置环境变量的脚本通常所做的。它允许您的脚本独立于调用者的 shell 和 shell 配置的变幻莫测而编写。

#!/bin/sh
FOO_DIR=$(dirname -- "$0")
cat <<EOF
FOO_DIR='$(printf %s "$FOO_DIR" | sed "s/'/'\\''/g")'
PATH="\$PATH:$FOO_DIR/bin";
export FOO_DIR PATH
EOF
Run Code Online (Sandbox Code Playgroud)

在调用者中: eval "`/path/to/setenv`"