Bash 脚本获取自身完整路径的可靠方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4774054/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:06:12  来源:igfitidea点击:

Reliable way for a Bash script to get the full path to itself

bashpath

提问by T.J. Crowder

I have a Bash script that needs to know its full path. I'm trying to find a broadly-compatible way of doing that without ending up with relative or funky-looking paths. I only need to support Bash, not sh, csh, etc.

我有一个需要知道其完整路径的 Bash 脚本。我试图找到一种广泛兼容的方式来做到这一点,而不会以相对或时髦的路径结束。我只需要支持 Bash,不需要 sh、csh 等。

What I've found so far:

到目前为止我发现了什么:

  1. The accepted answer to Getting the source directory of a Bash script from withinaddresses getting the path of the script via dirname $0, which is fine, but that may return a relativepath (like .), which is a problem if you want to change directories in the script and have the path still point to the script's directory. Still, dirnamewill be part of the puzzle.

  2. The accepted answer to Bash script absolute path with OS X(OS X specific, but the answer works regardless)gives a function that will test to see if $0looks relative and if so will pre-pend $PWDto it. But the result can still have relative bits in it (although overall it's absolute) — for instance, if the script is tin the directory /usr/binand you're in /usrand you type bin/../bin/tto run it (yes, that's convoluted), you end up with /usr/bin/../binas the script's directory path. Which works, but...

  3. The readlinksolution on this page, which looks like this:

    # Absolute path to this script. /home/user/bin/foo.sh
    SCRIPT=$(readlink -f 
    # Absolute path to this script. /home/user/bin/foo.sh
    SCRIPT=$(readlink -f 
    SCRIPTPATH="$( cd "$(dirname "
    SCRIPT=`realpath 
    SCRIPT=`realpath -s 
    ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
    
    ` SCRIPTPATH=`dirname $SCRIPT`
    ` SCRIPTPATH=`dirname $SCRIPT`
    ")" >/dev/null 2>&1 ; pwd -P )"
    ) # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT`
    ) # Absolute path this script is in. /home/user/bin SCRIPTPATH=`dirname $SCRIPT`

    But readlinkisn't POSIX and apparently the solution relies on GNU's readlinkwhere BSD's won't work for some reason (I don't have access to a BSD-like system to check).

  1. 地址中获取Bash 脚本的源目录的已接受答案通过获取脚本的路径dirname $0,这很好,但可能返回相对路径(如.),如果您想更改脚本并让路径仍然指向脚本的目录。尽管如此,dirname仍将是拼图的一部分。

  2. OS X 的 Bash 脚本绝对路径的公认答案(特定于 OS X,但无论如何答案都有效)提供了一个函数,该函数将测试是否$0看起来是相对的,如果是,则将预先挂起$PWD。但结果仍然可以有相对位在它(虽然总体来说是绝对的) -例如,如果脚本t中的目录/usr/bin和你是/usr,你键入bin/../bin/t运行它(是的,这是令人费解),你结了/usr/bin/../bin如脚本的目录路径。哪个有效,但是...

  3. 此页面上readlink解决方案,如下所示:

    DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
    

    readlink不是 POSIX,显然该解决方案依赖于 GNU readlink,其中 BSD 由于某种原因无法工作(我无法访问类似 BSD 的系统进行检查)。

So, various ways of doing it, but they all have their caveats.

所以,这样做的方法多种多样,但他们都有自己的警告。

What would be a better way? Where "better" means:

什么是更好的方法?“更好”的意思是:

  • Gives me the absolute path.
  • Takes out funky bits even when invoked in a convoluted way (see comment on #2 above). (E.g., at least moderately canonicalizes the path.)
  • Relies only on Bash-isms or things that are almost certain to be on most popular flavors of *nix systems (GNU/Linux, BSD and BSD-like systems like OS X, etc.).
  • Avoids calling external programs if possible (e.g., prefers Bash built-ins).
  • (Updated, thanks for the heads up, wich) It doesn't have to resolve symlinks (in fact, I'd kind of prefer it left them alone, but that's not a requirement).
  • 给我绝对路径。
  • 即使在以复杂的方式调用时也会删除时髦的位(请参阅上面对 #2 的评论)。(例如,至少适度规范化路径。)
  • 仅依赖 Bash 主义或几乎可以肯定是*nix 系统(GNU/Linux、BSD 和类似 BSD 的系统,如 OS X 等)的最流行风格的东西。
  • 如果可能,避免调用外部程序(例如,更喜欢 Bash 内置程序)。
  • 更新,感谢您的提醒,至极)它不必解析符号链接(实际上,我更喜欢将它们单独放置,但这不是必需的)。

采纳答案by T.J. Crowder

Here's what I've come up with (edit: plus some tweaks provided by sfstewman, levigroker, Kyle Strand, and Rob Kennedy), that seems to mostly fit my "better" criteria:

这是我想出的(编辑:加上sfstewmanlevigrokerKyle StrandRob Kennedy提供的一些调整),这似乎主要符合我的“更好”标准:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]); do
    cd "`dirname "${SCRIPT_PATH}"`"
    SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd  > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd   =[`pwd`]"

That SCRIPTPATHline seems particularly roundabout, but we need it rather than SCRIPTPATH=`pwd`in order to properly handle spaces and symlinks.

SCRIPTPATH条线看起来特别迂回,但我们需要它而不是SCRIPTPATH=`pwd`为了正确处理空格和符号链接。

The inclusion of output redirection (>/dev/null 2>&1) handles the rare(?) case where cdmight produce output that would interfere with the surrounding $( ... )capture. (Such as cdbeing overridden to also lsa directoryafter switching to it.)

输出重定向 ( >/dev/null 2>&1)的包含处理cd可能产生会干扰周围$( ... )捕获的输出的罕见 (?) 情况。(例如在切换到目录cd也被覆盖到ls目录。)

Note also that esoteric situations, such as executing a script that isn't coming from a file in an accessible file system at all (which is perfectly possible), is not catered to there (or in any of the other answers I've seen).

另请注意,深奥的情况,例如执行根本不是来自可访问文件系统中的文件的脚本(这是完全可能的),并没有迎合那里(或在我见过的任何其他答案中) )。

回答by Darshan Rivka Whittle

I'm surprised that the realpathcommand hasn't been mentioned here. My understanding is that it is widely portable / ported.

我很惊讶realpath这里没有提到该命令。我的理解是它具有广泛的便携性/移植性。

Your initial solution becomes:

您的初始解决方案变为:

wget -o /dev/null -O - http://host.domain/dir/script.sh |bash

And to leave symbolic links unresolved per your preference:

并根据您的喜好保留未解析的符号链接:

SCRIPT_PATH=$(dirname `which 
SCRIPT="$(readlink --canonicalize-existing "
#!/bin/bash
currentDir=$(
  cd $(dirname "
ORIGIN=$(dirname $(readlink -f 
CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
))
") pwd ) echo -n "current " pwd echo script $currentDir
")" SCRIPTPATH="$(dirname "$SCRIPT")"
`)

回答by Andrew Norrie

The simplest way that I have found to get a full canonical path in Bash is to use cdand pwd:

我发现在 Bash 中获得完整规范路径的最简单方法是使用cdpwd

realpath () {
  [[  = /* ]] && echo "" || echo "$PWD/${1#./}"
}

Using ${BASH_SOURCE[0]}instead of $0produces the same behavior regardless of whether the script is invoked as <name>or source <name>.

无论脚本是作为还是调用,使用${BASH_SOURCE[0]}代替 都会$0产生相同的行为。<name>source <name>

回答by Felix Rabe

I just had to revisit this issue today and found Get the source directory of a Bash script from within the script itself:

我今天只需要重新审视这个问题,发现从脚本本身中获取 Bash 脚本的源目录

BASEDIR=$(readlink -f ##代码## | xargs dirname)

There's more variants at the linked answer, e.g. for the case where the script itself is a symlink.

链接的答案有更多变体,例如脚本本身是符号链接的情况。

回答by GreenFox

Get the absolute path of a shell script

获取shell脚本的绝对路径

It does not use the -foption in readlink, and it should therefore work on BSD/Mac OS X.

它不使用-freadlink 中的选项,因此它应该适用于 BSD/Mac OS X。

Supports

支持

  • source ./script (When called by the .dot operator)
  • Absolute path /path/to/script
  • Relative path like ./script
  • /path/dir1/../dir2/dir3/../script
  • When called from symlink
  • When symlink is nested eg) foo->dir1/dir2/bar bar->./../doe doe->script
  • When caller changes the scripts name
  • source ./script(当由.点运算符调用时)
  • 绝对路径 /path/to/script
  • /script 之类的相对路径
  • /path/dir1/../dir2/dir3/../script
  • 从符号链接调用时
  • 当符号链接嵌套时,例如) foo->dir1/dir2/bar bar->./../doe doe->script
  • 当调用者更改脚本名称时

I am looking for corner cases where this code does not work. Please let me know.

我正在寻找此代码不起作用的极端情况。请告诉我。

Code

代码

##代码##

Known issus

已知问题

The script must be on disk somewhere. Let it be over a network. If you try to run this script from a PIPE it will not work

脚本必须在磁盘上的某个地方。让它通过网络。如果您尝试从 PIPE 运行此脚本,它将不起作用

##代码##

Technically speaking, it is undefined. Practically speaking, there is no sane way to detect this. (A co-process can not access the environment of the parent.)

从技术上讲,它是未定义的。实际上,没有理智的方法可以检测到这一点。(协同进程不能访问父进程的环境。)

回答by Matt

Use:

用:

##代码##

whichprints to standard output the full path of the executable that would have been executed when the passed argument had been entered at the shell prompt (which is what $0 contains)

which将在 shell 提示符下输入传递的参数时将执行的可执行文件的完整路径打印到标准输出(这是 $0 包含的内容)

dirnamestrips the non-directory suffix from a file name.

dirname从文件名中去除非目录后缀。

Hence you end up with the full path of the script, no matter if the path was specified or not.

因此,无论是否指定了路径,您最终都会得到脚本的完整路径。

回答by ypid

As realpath is not installed per default on my Linux system, the following works for me:

由于我的 Linux 系统上默认没有安装 realpath,以下对我有用:

##代码##

$SCRIPTwill contain the real file path to the script and $SCRIPTPATHthe real path of the directory containing the script.

$SCRIPT将包含脚本的真实文件路径和$SCRIPTPATH包含脚本的目录的真实路径。

Before using this read the comments of this answer.

在使用之前阅读这个答案的评论。

回答by gerardw

Easy to read? Below is an alternative. It ignores symlinks

易于阅读?下面是一个替代方案。它忽略符号链接

##代码##

Since I posted the above answer a couple years ago, I've evolved my practice to using this linux specific paradigm, which properly handles symlinks:

自从几年前我发布了上述答案以来,我已经将我的实践发展为使用这个特定于 linux 的范式,它可以正确处理符号链接:

##代码##

回答by kenorb

You may try to define the following variable:

您可以尝试定义以下变量:

##代码##

Or you can try the following function in Bash:

或者您可以在 Bash 中尝试以下功能:

##代码##

This function takes one argument. If the argument already has an absolute path, print it as it is, otherwise print $PWDvariable + filename argument (without ./prefix).

这个函数接受一个参数。如果参数已经有绝对路径,则按原样打印,否则打印$PWD变量 + 文件名参数(无./前缀)。

Related:

有关的:

回答by poussma

Simply:

简单地:

##代码##

Fancy operators are not needed.

不需要花哨的运算符。