查找源 shell 脚本的位置

查找源 shell 脚本的位置

源 shell 脚本是否可能知道其位置?我读了确定源 shell 脚本的路径但如果使用 POSIX shell,答案集中在bash和以及失败上。tcsh$0也不是解决方案并产生错误的结果

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

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

# within “thescript”
-> /tmp/foo/bar/somedir

一些背景:bin该脚本是现有应用程序的一部分,在相对于源脚本的已知位置(因体系结构而异)的目录中包含数十个二进制文件。要使用该应用程序,用户需要将bin目录添加到 的脚本PATH中,以便可以在当前 shell(无论是哪个 shell)中轻松调用应用程序的二进制文件。

答案1

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

env -i PATH=/usr/bin:/bin sh -c '. ./included.sh' | grep included

其中included.sh包含

echo "$0"
set

在 bash 中,源脚本的名称位于$BASH_SOURCE.在 zsh 中(在 zsh 兼容模式下,而不是在 sh 或 ksh 兼容模式下),它是 in $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

您可以通过查看 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

如果脚本源自使用重定向的复杂脚本,则该脚本可能不是具有最高编号描述符的文件。您可能想要循环浏览打开的文件。无论如何,这并不能保证有效。定位源脚本的唯一可靠方法是使用 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

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

答案2

添加到 Gilles 答案中,您可以if $0 = ash通过/proc/$$ interface.我不知道这有多准确,但/proc/$$/fd/11似乎总是用 BusyBox 的灰指向 this_script 。

将此作为一个潜在的答案提供给那些浏览此页面的人(就像我一样)。

最后一个答案已被删除——因此,我对这项工作的主张是在 4 个不同的硬件平台(x86、ARM、XLP 和 powerpc)上进行了测试。所有人都给出相同的 fd/11 答案。来自的阅读链接/proc/$$/fd/11产生我的脚本。

安迪

相关内容