什么平台独立的方式来在外壳脚本中查找 shell 可执行文件的目录



根据POSIX:

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html

在某些情况下,它并不明显。例如:

If the file is not in the current working directory,
the implementation may perform a search for an executable
file using the value of PATH, as described in Command Search and Execution.

我的 Bash 4.x 不遵循此可选规则(出于安全问题??),所以我无法测试它在现实生活中的情况......

什么平台独立的方式可以在shell脚本中查找shell可执行文件的目录?

PS.此外dirname $0情况失败,并显示:

#!/bin/sh
echo $0
dirname $0

当您:

$ sh runme.sh
runme.sh
.

所以你需要这样的东西:

CMDPATH=`cd $(dirname $0); echo $PWD`

为了使代码仅依赖于内置的 shell 功能,我将代码重写为:

PREVPWD=$PWD
cd ${0%${0##*/}}.
CMDPATH=$PWD
cd $PREVPWD

这看起来很丑,但不需要分叉任何可执行文件......

EDIT3:

虽然还不是严格的POSIX,但realpath自2012年以来一直是GNU核心应用程序。完全披露:在我info coreutils目录中注意到它并立即想到这个问题之前,我从未听说过它,但是使用以下演示的函数应该可靠地(很快POSIXLY?),并且我希望,有效地为其调用方提供绝对来源$0

% _abs_0() { 
> o1="${1%%/*}"; ${o1:="${1}"}; ${o1:=`realpath -s "${1}"`}; eval "$1=${o1}"; 
> }  
% _abs_0 ${abs0:="${0}"} ; printf %s\n "${abs0}"
/no/more/dots/in/your/path2.sh

EDIT4:值得强调的是,此解决方案使用 POSIX 参数扩展来首先检查路径是否真的需要扩展和解析,然后再尝试这样做。这应该通过信使变量返回一个绝对来源的$0(值得注意的是-s将保留symlinks),就像我想象的那样有效地完成,无论路径是否已经是绝对的。

编辑2:

现在我相信我更好地理解了你的问题,不幸的是,这实际上使下面的大部分内容变得无关紧要。

小编辑:在文档中找到realpath之前,我至少已经削减了我的版本,不依赖于时间字段,但是,公平的警告,在测试了一些之后,我不太相信ps在其命令路径扩展能力方面是完全可靠的)

另一方面,您可以这样做:

ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"
eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"

我需要修复它以更好地处理字段,而不是期望时间字段在进程路径之前出现并依赖其包含的冒号作为参考,特别是因为这不适用于进程路径中的冒号,但我认为这是微不足道的,很快就会发生。我相信该功能在其他方面符合 POSIX 标准。我认为,仅参数扩展就可以完成必要的工作。

不严格相关(或正确):

这应该适用于符合 POSIX 指南的所有情况:

echo ${0%/*}

编辑:

所以我承认,至少乍一看,我不完全理解你描述的问题。显然,在您的问题中,您表现出对通过参数扩展进行变量字符串操作的 POSIX 标准的熟悉程度(即使您的特定实现乍一看似乎有点紧张),因此我在解释您的问题时可能缺少一些重要信息,也许,至少以目前的形式,这不是您想要的答案。

我之前已经发布了关于内联变量 null/set 测试的参数扩展的文章,这些测试可能对您有用,也可能没有用,正如您在"检查 Shell 变量空性的便携式方法"问题中看到的那样。我之所以提到这一点,主要是因为我的回答在很大程度上是从 POSIX 参数扩展指南中复制/粘贴的,包括指向该主题指南覆盖范围的锚定链接,以及来自规范文档和我自己可能不太专业演示的结构的一些示例。

然而,我坦率地承认,虽然我还不完全理解你问的是什么,但我不相信你会在那里找到一个具体的答案。相反,我怀疑您可能已经忘记了,就像我偶尔所做的那样,POSIX 字符串操作中的#%运算符用于指定要删除的字符串部分,而不是您希望保留的部分,因为有些人可能会发现更直观。我的意思是,您以这种方式搜索的任何字符串切片都旨在从输出中消失,然后,在删除指定的搜索字符串后,该输出将仅是原始字符串的剩余部分。

所以这里有一些概述:

而任何一个运算符的单个实例只会删除尽可能少的搜索以完全满足您的搜索,但是当双重实例化时,搜索将以贪婪的形式调用,并删除搜索可能允许的尽可能多的原始字符串。

除此之外,您只需要知道一些基本的正则表达式,并记住#左侧开始搜索您的删除字符串并向右扫描,而%右侧开始搜索并向左扫描。

## short example before better learning if I'm on the right track
## demonstrating path manipulation with '#' and '%'
% _path_one='/one/two/three/four.five'
% _path_two='./four.five'
## short searching from the right with our wildcard to the right 
## side of a single character removes everything to the right of 
## of the specified character and the character itself
## this is a very simple means of stripping extensions and paths
% echo ${_path_one%.*} ${_path_one%/*}
/one/two/three/four   /one/two/three
## long searching from the left with the wildcard to the left of
## course produces opposite results 
% echo ${_path_one##*.} ${_path_one##*/}
five    four.five
## will soon come back to show more probably
我相信

您可以使用readlink来获取它

scriptPath=$(readlink -f -- "$0")
scriptDirectory=${scriptPath%/*}

最新更新