5

根据 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 可执行文件目录的独立于平台的方法是什么?

附言。案例也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

这看起来很难看,但不需要 fork 任何可执行文件......

4

2 回答 2

5

编辑3:

虽然还不是严格意义上的 POSIX,但realpath是自 2012 年以来的 GNU 核心应用程序。完全披露:在我在 TOC 中注意到它并立即想到这个问题之前从未听说过它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 将保留的显着例外) ,-ssymlinks无论路径是否已经是绝对的,它都可以完成。

编辑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 字符串操作中的#and%运算符用于指定要删除的字符串部分,而不是您希望保留的部分,因为有些人可能会觉得更直观。我的意思是您以这种方式搜索的任何字符串切片都被设计为从您的输出中消失,这将只是您在删除指定的搜索字符串后保留的原始字符串。

所以这里有一个概述:

虽然任一运算符的单个实例将仅删除尽可能少的内容以完全满足您的搜索,但当双重实例时,搜索以贪婪的形式调用,并删除尽可能多的原始字符串,因为您的搜索可能允许

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

## 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
于 2013-11-22T08:03:27.673 回答
1

我相信你可以使用它readlink

scriptPath=$(readlink -f -- "$0")
scriptDirectory=${scriptPath%/*}
于 2013-10-11T08:11:03.137 回答