查找源shell脚本的位置


8

源shell脚本是否可能知道其位置?我已经阅读了确定源shell脚本的路径,但是答案集中于bashtcsh如果使用POSIX shell,则失败。$0也不是解决方案,并且会产生错误的结果

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

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

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

背景知识:该脚本是现有应用程序的一部分,该应用程序在bin相对于源脚本的已知位置(每个体系结构有所不同)的目录中包含数十个二进制文件。要使用该应用程序,用户需要获取将bin目录添加到的脚本PATH,以便可以在当前外壳程序(可能是任何一个外壳程序)中轻松调用应用程序的二进制文件。


请一次检查此链接可能是您正在寻找的对象paste.ubuntu.com/6242655
Rahul Patil

@RahulPatil只能在bash上使用,并且高度不可移植。
Marco Marco

1
我很确定答案是否定的。您仅找到特定于Shell的方法,因为没有可移植的方法。您需要什么呢?您可以告诉脚本用户执行. /path/to/script诸如MARCO_DIR=/path/to; . $MARCO_DIR/script或之类的操作eval "$(/path/to/script)"吗?@RahulPatil BASH_SOURCE显然是bash特有的。
吉尔(Gilles)'所以

@Gilles正如我所说,它不必非常强大。但是我还没有找到一种方法可以在POSIX shell中的最简单情况下远程工作。(所以暂时的解决办法是,使用这个应用程序的需要支持的炮弹之一。)随意张贴“不......”作为答案,如果这就是答案。
Marco Marco

@Gilles不能更改应用程序的设置方式。它将破坏安装。这就是为什么我希望在不更改脚本调用方式的情况下使脚本更加健壮。最后,它仅设置了PATH,因此,如果脚本失败,则后备方法是找到二进制文件并将其路径添加到环境中。
Marco Marco

Answers:


9

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

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

哪里included.sh包含

echo "$0"
set

在bash中,源脚本的名称在中$BASH_SOURCE。在zsh中(在zsh兼容模式下,而不是在sh或ksh兼容模式下),它在其中$0(请注意,在函数中,$0是函数名)。在pdksh和破折号中,它不可用。在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

您可以通过查看外壳程序打开了哪些文件来尝试猜测脚本的位置。实验上,这似乎适用于dash和pdksh,但不适用于bash或ksh93,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`"


0

在Gilles的答案中,您也许可以通过获得shell脚本(if $0 = ash/proc/$$ interface。我不知道这有多准确,但是/proc/$$/fd/11似乎总是指向带有BusyBox灰烬的this_script。

向访问此页面的用户提供此潜在答案(我也是)。

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

安迪

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.