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

时间:2013-10-11 07:54:51

标签: bash shell cross-platform posix sh

根据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.

My 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

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

2 个答案:

答案 0 :(得分:4)

<强> EDIT3:

虽然还不是严格的POSIX,realpath 是自2012年以来的GNU核心应用。完全披露:在info coreutils TOC中注意到它之前从未听说过它,并立即想到了这个问题,但是使用所演示的以下函数应该可靠,(很快就是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 parameter expansion首先检查路径是否确实需要在尝试之前进行扩展和解析。这应该通过信使变量 返回绝对来源的$0-ssymlinks} 保留为{<1>}的明显例外强大>有效我可以想象它可以完成路径是否已经绝对。

<强> EDIT2:

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

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

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

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

eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"

我需要修复它以便更好地使用字段而不是期望时间字段恰好在进程的路径之前并依赖于其包含的冒号作为引用,特别是因为这不适用于进程路径中的冒号,但我认为这很简单,很快就会发生。我相信,该功能符合POSIX标准。我认为可能只有参数扩展可以做必要的事情。

不严格相关(或正确):

这应该适用于符合POSIX准则的每种情况:

echo ${0%/*}

修改

所以我承认,至少乍一看,我并不完全理解你描述的问题。显然在您的问题中,您通过参数扩展展示了对变量字符串操作的POSIX标准的熟悉程度(即使您的特定实现看起来有点紧张),所以在我对您的问题的解释中,我可能会遗漏一些重要的信息也许,至少在目前的形式中,这不是你寻求的答案。

我之前已经发布了内联变量null / set测试的参数扩展,这可能会对您有用,也可能没有用,正如您在“Portable Way to Check Emptiness of a Shell Variable”问题中看到的那样。我之所以提到这一点,主要是因为我的答案在很大程度上是从参数扩展的POSIX指南中复制/粘贴的,包括anchored link关于这个主题的指南报道,以及规范文档和我自己的一些例子。也许不那么专业地证明了结构。

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

所以这里有一点概述:

的单个实例只会尽可能删除 以完全满足您的搜索,但加倍时instanced 贪婪形式调用搜索, 删除原始字符串 ,因为您的搜索可能允许

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

## 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

答案 1 :(得分:1)

我相信你可以使用readlink:

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