如何从脚本本身获取 Bash 脚本所在的目录?

发布于 2024-07-04 18:42:24 字数 253 浏览 8 评论 0原文

如何获取 Bash 脚本所在目录的路径, 在该脚本内

我想使用 Bash 脚本作为另一个应用程序的启动器。 我想将工作目录更改为 Bash 脚本所在的目录,这样我就可以对该目录中的文件进行操作,如下所示:

$ ./application

How do I get the path of the directory in which a Bash script is located, inside that script?

I want to use a Bash script as a launcher for another application. I want to change the working directory to the one where the Bash script is located, so I can operate on the files in that directory, like so:

$ ./application

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

难理解 2024-07-11 18:42:25

尝试以下交叉兼容解决方案:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

因为 realpathreadlink 等命令可能不可用(取决于操作系统)。

注意:在 Bash 中,建议使用 ${BASH_SOURCE[0]} 而不是 $0,否则在获取文件时路径可能会中断(source/<代码>.)。

或者,您可以在 Bash 中尝试以下函数:

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

该函数采用一个参数。 如果参数已经有绝对路径,则按原样打印它,否则打​​印 $PWD 变量 + 文件名参数(不带 ./ 前缀)。

相关:

Try the following cross-compatible solution:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

As the commands such as realpath or readlink could be not available (depending on the operating system).

Note: In Bash, it's recommended to use ${BASH_SOURCE[0]} instead of $0, otherwise path can break when sourcing the file (source/.).

Alternatively you can try the following function in Bash:

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

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

Related:

甲如呢乙后呢 2024-07-11 18:42:25

无论您如何调用脚本,都可以如此简单:

#!/bin/bash
#

the_source=$(readlink -f ${BASH_SOURCE[0]})
the_dirname=$(dirname ${the_source})

echo "the_source: ${the_source}"
echo "the_dirname: ${the_dirname}"

从任何地方运行:

user@computer:~/Downloads/temp$ ./test.sh 

输出:

the_source: /home/user/Downloads/temp/test.sh
the_dirname: /home/user/Downloads/temp

Incredible how simple can be, no matter how you call the script:

#!/bin/bash
#

the_source=$(readlink -f ${BASH_SOURCE[0]})
the_dirname=$(dirname ${the_source})

echo "the_source: ${the_source}"
echo "the_dirname: ${the_dirname}"

Run from anywhere:

user@computer:~/Downloads/temp$ ./test.sh 

Output:

the_source: /home/user/Downloads/temp/test.sh
the_dirname: /home/user/Downloads/temp
梦醒时光 2024-07-11 18:42:25

$_ 值得一提作为 $0 的替代品。 如果您从 Bash 运行脚本,则接受的答案可以缩短为:

DIR="$( dirname "$_" )"

请注意,这必须是脚本中的第一个语句。

$_ is worth mentioning as an alternative to $0. If you're running a script from Bash, the accepted answer can be shortened to:

DIR="$( dirname "$_" )"

Note that this has to be the first statement in your script.

白衬杉格子梦 2024-07-11 18:42:25

对于具有 GNU coreutils readlink 的系统(例如 Linux):

$(readlink -f "$(dirname "$0")")

$0 包含脚本文件名时,无需使用 BASH_SOURCE

For systems having GNU coreutils readlink (for example, Linux):

$(readlink -f "$(dirname "$0")")

There's no need to use BASH_SOURCE when $0 contains the script filename.

唠甜嗑 2024-07-11 18:42:25

我会使用这样的东西:

# Retrieve the full pathname of the called script
scriptPath=$(which $0)

# Check whether the path is a link or not
if [ -L $scriptPath ]; then

    # It is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # Otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

I would use something like this:

# Retrieve the full pathname of the called script
scriptPath=$(which $0)

# Check whether the path is a link or not
if [ -L $scriptPath ]; then

    # It is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # Otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi
趁微风不噪 2024-07-11 18:42:25

这是对 他们的答案

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}

这应该仍然适用于他们列出的所有情况。

这将阻止 pushd 失败后的 popd。 感谢 konsolebox。

This is a slight revision to the solution e-satis and 3bcdnlklvc04a pointed out in their answer:

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}

This should still work in all the cases they listed.

This will prevent popd after a failed pushd. Thanks to konsolebox.

欲拥i 2024-07-11 18:42:25

这些是获取脚本信息的简短方法:

文件夹和文件:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

使用这些命令:

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

我得到了以下输出:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

另请参阅: https:// /pastebin.com/J8KjxrPF

These are short ways to get script information:

Folders and files:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

Using these commands:

    echo Script-Dir : `dirname "$(realpath $0)"`
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : `basename "$(realpath $0)"`
    echo Script-Name : `basename $0`
    echo
    echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
    echo Script-Dir-Relative : `dirname $0`
    echo
    echo Calling-Dir : `pwd`

And I got this output:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

Also see: https://pastebin.com/J8KjxrPF

胡渣熟男 2024-07-11 18:42:25

这适用于 Bash 3.2:

path="$( dirname "$( which "$0" )" )"

如果您的 $PATH 中有一个 ~/bin 目录,那么该目录中就有 A 。 它获取脚本~/bin/lib/B。 您知道包含的脚本相对于原始脚本在 lib 子目录中的位置,但不知道它相对于用户当前目录的位置。

这是通过以下方式解决的(在 A 内):

source "$( dirname "$( which "$0" )" )/lib/B"

用户在哪里或他/她如何调用脚本并不重要。 这永远有效。

This works in Bash 3.2:

path="$( dirname "$( which "$0" )" )"

If you have a ~/bin directory in your $PATH, you have A inside this directory. It sources the script ~/bin/lib/B. You know where the included script is relative to the original one, in the lib subdirectory, but not where it is relative to the user's current directory.

This is solved by the following (inside A):

source "$( dirname "$( which "$0" )" )/lib/B"

It doesn't matter where the user is or how he/she calls the script. This will always work.

风渺 2024-07-11 18:42:25

我尝试了所有这些,但没有一个起作用。 其中一个非常接近,但它有一个小错误,严重损坏了它; 他们忘记将路径用引号引起来。

另外,很多人认为您是从 shell 运行脚本,因此他们忘记了当您打开新脚本时,它默认为您的家。

尝试此目录的大小:

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

无论您如何或在何处运行它,它都是正确的:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

因此,为了使其真正有用,以下是如何更改到正在运行的脚本的目录:

cd "`dirname "$0"`"

I tried all of these and none worked. One was very close, but it had a tiny bug that broke it badly; they forgot to wrap the path in quotation marks.

Also a lot of people assume you're running the script from a shell, so they forget when you open a new script it defaults to your home.

Try this directory on for size:

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

This gets it right regardless how or where you run it:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

So to make it actually useful, here's how to change to the directory of the running script:

cd "`dirname "$0"`"
等待我真够勒 2024-07-11 18:42:25
#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`
#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`
香草可樂 2024-07-11 18:42:25

尝试使用:

real=$(realpath "$(dirname "$0")")

Try using:

real=$(realpath "$(dirname "$0")")
心舞飞扬 2024-07-11 18:42:25

这是简单、正确的方法:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

说明:

  • ${BASH_SOURCE[0]} - 脚本的完整路径。 即使在获取脚本时,该值也是正确的,例如 source <(echo 'echo $0') 打印 bash,同时将其替换为 ${BASH_SOURCE[0]} 将打印脚本的完整路径。 (当然,这假设您可以依赖 Bash。)

  • readlink -f - 递归解析指定路径中的任何符号链接。 这是一个 GNU 扩展,在(例如)BSD 系统上不可用。 如果您运行的是 Mac,则可以使用 Homebrew 安装 GNU coreutils 并用 greadlink -f 替换它。

  • 当然dirname获取路径的父目录。

Here is the simple, correct way:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

Explanation:

  • ${BASH_SOURCE[0]} - the full path to the script. The value of this will be correct even when the script is being sourced, e.g. source <(echo 'echo $0') prints bash, while replacing it with ${BASH_SOURCE[0]} will print the full path of the script. (Of course, this assumes you're OK taking a dependency on Bash.)

  • readlink -f - Recursively resolves any symlinks in the specified path. This is a GNU extension, and not available on (for example) BSD systems. If you're running a Mac, you can use Homebrew to install GNU coreutils and supplant this with greadlink -f.

  • And of course dirname gets the parent directory of the path.

自在安然 2024-07-11 18:42:25

对于 Python,请参阅我的其他答案

对于 Bash,请参见下文:

摘要:

FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"

# OR, if you do NOT need it to work for **sourced** scripts too:
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"

# OR, depending on which path you want, in case of nested `source` calls
# FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[0]}")"

# OR, add `-s` to NOT expand symlinks in the path:
# FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"

SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"

详细信息:

如何获取任何脚本的完整文件路径完整目录基本文件名 em>run OR sourced...

...即使从另一个 bash 函数或脚本中调用被调用的脚本,或者使用嵌套源时!

在许多情况下,您需要获取的只是刚刚调用的脚本的完整路径。 这可以使用realpath轻松完成。 请注意,realpathGNU coreutils 的一部分。 如果您还没有安装它(Ubuntu 上默认安装),您可以使用 sudo apt update && 来安装它。 sudo apt install coreutils。

get_script_path.sh(有关此脚本的最新版本,请参阅 get_script_path.sh 在我的 eRCaGuy_hello_world 存储库中):

#!/bin/bash

# A. Obtain the full path, and expand (walk down) symbolic links
# A.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# A.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
# B.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "$0")"
# B.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "${BASH_SOURCE[-1]}")"

# You can then also get the full path to the directory, and the base
# filename, like this:
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"

# Now print it all out
echo "FULL_PATH_TO_SCRIPT = \"$FULL_PATH_TO_SCRIPT\""
echo "SCRIPT_DIRECTORY    = \"$SCRIPT_DIRECTORY\""
echo "SCRIPT_FILENAME     = \"$SCRIPT_FILENAME\""

重要说明关于嵌套source调用如果上面的“${BASH_SOURCE[-1]}”没有给你你想要的东西如果需要,请尝试使用 "${BASH_SOURCE[0]}" 代替。 第一个 (0) 索引为您提供数组中的第一个条目,最后一个 (-1) 索引为您提供数组中的最后一个条目。 根据您想要的内容,您可能实际上想要第一个条目。 当我使用 获取 ~/.bashrc 时,我发现了这种情况。 ~/.bashrc,它使用 获取 ~/.bash_aliases。 ~/.bash_aliases,我想要 ~/.bash_aliases 文件的 realpath (带有扩展的符号链接),而不是 ~/. bashrc 文件。 由于这些是嵌套 source 调用,因此使用 "${BASH_SOURCE[0]}" 给了我我想要的:的扩展路径>~/.bash_aliases! 然而,使用 "${BASH_SOURCE[-1]}" 却给了我想要的东西:~/.bashrc 的扩展路径 >。

命令和输出示例:

  1. 运行脚本:
    ~/GS/dev/eRCaGuy_hello_world/bash$ ./get_script_path.sh  
      FULL_PATH_TO_SCRIPT =“/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh” 
      SCRIPT_DIRECTORY =“/home/gabriel/GS/dev/eRCaGuy_hello_world/bash” 
      SCRIPT_FILENAME =“get_script_path.sh” 
      
  2. 使用 获取脚本。 get_script_path.shsource get_script_path.sh (结果与上面完全相同,因为我在脚本而不是 "$0"):
    ~/GS/dev/eRCaGuy_hello_world/bash$ 。   获取脚本路径.sh  
      FULL_PATH_TO_SCRIPT =“/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh” 
      SCRIPT_DIRECTORY =“/home/gabriel/GS/dev/eRCaGuy_hello_world/bash” 
      SCRIPT_FILENAME =“get_script_path.sh” 
      

如果您在脚本中使用 "$0" 而不是 "${BASH_SOURCE[-1]}",则在运行时您将获得与上面相同的输出 脚本,但是当采购脚本时,会出现这个不需要的输出:

~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh 
FULL_PATH_TO_SCRIPT               = "/bin/bash"
SCRIPT_DIRECTORY                  = "/bin"
SCRIPT_FILENAME                   = "bash"

并且,显然如果您使用 "$BASH_SOURCE" 而不是“${BASH_SOURCE[-1]}”,如果从另一个 bash 函数中调用脚本,它将无法工作。 因此,使用 "${BASH_SOURCE[-1]}" 是最好的方法,因为它解决了这两个问题! 请参阅下面的参考资料。

realpath 和 realpath -s 之间的区别:

请注意,realpath 还可以成功地沿着符号链接确定并指向他们的目标而不是指向符号链接。 如果您不想要这种行为(有时我不想要),则将 -s 添加到上面的 realpath 命令中,使该行看起来像这样:

# Obtain the full path, but do NOT expand (walk down) symbolic links; in
# other words: **keep** the symlinks as part of the path!
FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"

这样,符号链接不会扩展。 相反,它们保留原样,作为完整路径中的符号链接。

上面的代码现在是我的 eRCaGuy_hello_world 存储库的一部分,在此文件中:bash/get_script_path.sh。 参考并运行此文件以获取路径中带有和不带有符号链接的完整示例。 有关两种情况下的输出示例,请参阅文件底部。

参考文献:

  1. 如何检索给定相对路径的绝对路径
  2. 教会了我有关BASH_SOURCE变量的信息:Unix 和 Linux Linux:确定源shell脚本的路径
  3. 告诉我,BASH_SOURCE实际上是一个数组,我们希望它的最后一个元素能够在函数内按预期工作(因此我使用"${BASH_SOURCE[-1]}" 在我的代码中): Unix & Linux:确定源 shell 脚本的路径
  4. man bash --> 搜索 BASH_SOURCE
    <块引用>

    BASH_SOURCE

    一个数组变量,其成员是源文件名,其中定义了 FUNCNAME 数组变量中相应的 shell 函数名称。 shell 函数 ${FUNCNAME[$i]} 在文件 ${BASH_SOURCE[$i]} 中定义,并从 ${BASH_SOURCE[$i] 调用+1]}

另请参阅:

  1. 我对Python的回答:如何获取当前正在执行的python文件的路径和名称?
  2. [我的回答] Unix 和 Linux Linux:确定源 shell 脚本的路径

For Python, see my other answer here.

For Bash, see below:

Summary:

FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"

# OR, if you do NOT need it to work for **sourced** scripts too:
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"

# OR, depending on which path you want, in case of nested `source` calls
# FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[0]}")"

# OR, add `-s` to NOT expand symlinks in the path:
# FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"

SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"

Details:

How to obtain the full file path, full directory, and base filename of any script being run OR sourced...

...even when the called script is called from within another bash function or script, or when nested sourcing is being used!

For many cases, all you need to acquire is the full path to the script you just called. This can be easily accomplished using realpath. Note that realpath is part of GNU coreutils. If you don't have it already installed (it comes default on Ubuntu), you can install it with sudo apt update && sudo apt install coreutils.

get_script_path.sh (for the latest version of this script, see get_script_path.sh in my eRCaGuy_hello_world repo):

#!/bin/bash

# A. Obtain the full path, and expand (walk down) symbolic links
# A.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# A.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
# B.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "$0")"
# B.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "${BASH_SOURCE[-1]}")"

# You can then also get the full path to the directory, and the base
# filename, like this:
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"

# Now print it all out
echo "FULL_PATH_TO_SCRIPT = \"$FULL_PATH_TO_SCRIPT\""
echo "SCRIPT_DIRECTORY    = \"$SCRIPT_DIRECTORY\""
echo "SCRIPT_FILENAME     = \"$SCRIPT_FILENAME\""

IMPORTANT note on nested source calls: if "${BASH_SOURCE[-1]}" above doesn't give you quite what you want, try using "${BASH_SOURCE[0]}" instead. The first (0) index gives you the first entry in the array, and the last (-1) index gives you the last last entry in the array. Depending on what it is you're after, you may actually want the first entry. I discovered this to be the case when I sourced ~/.bashrc with . ~/.bashrc, which sourced ~/.bash_aliases with . ~/.bash_aliases, and I wanted the realpath (with expanded symlinks) to the ~/.bash_aliases file, NOT to the ~/.bashrc file. Since these are nested source calls, using "${BASH_SOURCE[0]}" gave me what I wanted: the expanded path to ~/.bash_aliases! Using "${BASH_SOURCE[-1]}", however, gave me what I did not want: the expanded path to ~/.bashrc.

Example command and output:

  1. Running the script:
    ~/GS/dev/eRCaGuy_hello_world/bash$ ./get_script_path.sh 
    FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
    SCRIPT_DIRECTORY    = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
    SCRIPT_FILENAME     = "get_script_path.sh"
    
  2. Sourcing the script with . get_script_path.sh or source get_script_path.sh (the result is the exact same as above because I used "${BASH_SOURCE[-1]}" in the script instead of "$0"):
    ~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh 
    FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
    SCRIPT_DIRECTORY    = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
    SCRIPT_FILENAME     = "get_script_path.sh"
    

If you use "$0" in the script instead of "${BASH_SOURCE[-1]}", you'll get the same output as above when running the script, but this undesired output instead when sourcing the script:

~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh 
FULL_PATH_TO_SCRIPT               = "/bin/bash"
SCRIPT_DIRECTORY                  = "/bin"
SCRIPT_FILENAME                   = "bash"

And, apparently if you use "$BASH_SOURCE" instead of "${BASH_SOURCE[-1]}", it will not work if the script is called from within another bash function. So, using "${BASH_SOURCE[-1]}" is therefore the best way to do it, as it solves both of these problems! See the references below.

Difference between realpath and realpath -s:

Note that realpath also successfully walks down symbolic links to determine and point to their targets rather than pointing to the symbolic link. If you do NOT want this behavior (sometimes I don't), then add -s to the realpath command above, making that line look like this instead:

# Obtain the full path, but do NOT expand (walk down) symbolic links; in
# other words: **keep** the symlinks as part of the path!
FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"

This way, symbolic links are NOT expanded. Rather, they are left as-is, as symbolic links in the full path.

The code above is now part of my eRCaGuy_hello_world repo in this file here: bash/get_script_path.sh. Reference and run this file for full examples both with and withOUT symlinks in the paths. See the bottom of the file for example output in both cases.

References:

  1. How to retrieve absolute path given relative
  2. taught me about the BASH_SOURCE variable: Unix & Linux: determining path to sourced shell script
  3. taught me that BASH_SOURCE is actually an array, and we want the last element from it for it to work as expected inside a function (hence why I used "${BASH_SOURCE[-1]}" in my code here): Unix & Linux: determining path to sourced shell script
  4. man bash --> search for BASH_SOURCE:

    BASH_SOURCE

    An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME array variable are defined. The shell function ${FUNCNAME[$i]} is defined in the file ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}.

See also:

  1. My answer for Python: How do I get the path and name of the python file that is currently executing?
  2. [my answer] Unix & Linux: determining path to sourced shell script
坠似风落 2024-07-11 18:42:25

这是符合 POSIX 标准的单行代码:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

Here is a POSIX compliant one-liner:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH
围归者 2024-07-11 18:42:25

最短、最优雅的方法是:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

这适用于所有平台并且超级干净。

更多详细信息可以在“bash 脚本位于哪个目录?”中找到。

The shortest and most elegant way to do this is:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

This would work on all platforms and is super clean.

More details can be found in "Which directory is that bash script in?".

一场春暖 2024-07-11 18:42:25

这是 Linux 特定的,但您可以使用:

SELF=$(readlink /proc/$/fd/255)

This is Linux specific, but you could use:

SELF=$(readlink /proc/$/fd/255)
相思故 2024-07-11 18:42:25
$(dirname "$(readlink -f "$BASH_SOURCE")")
$(dirname "$(readlink -f "$BASH_SOURCE")")
败给现实 2024-07-11 18:42:25

这将获取 Mac OS X v10.6.6 (Snow Leopard) 上的当前工作目录:

DIR=$(cd "$(dirname "$0")"; pwd)

This gets the current working directory on Mac OS X v10.6.6 (Snow Leopard):

DIR=$(cd "$(dirname "$0")"; pwd)
方觉久 2024-07-11 18:42:25
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
空宴 2024-07-11 18:42:25

我认为这并不像其他人想象的那么容易。 pwd 不起作用,因为当前目录不一定是脚本所在的目录。 $0 也不总是具有该信息。 请考虑以下三种调用脚本的方法:

./script

/usr/bin/script

script

在第一种和第三种方法中,$0 没有完整路径信息。 在第二个和第三个中,pwd不起作用。 以第三种方式获取目录的唯一方法是运行路径并找到具有正确匹配的文件。 基本上,代码必须重做操作系统所做的事情。

执行您所要求的操作的一种方法是仅对 /usr/share 目录中的数据进行硬编码,并通过其完整路径引用它。 无论如何,数据不应该位于 /usr/bin 目录中,所以这可能就是要做的事情。

I don't think this is as easy as others have made it out to be. pwd doesn't work, as the current directory is not necessarily the directory with the script. $0 doesn't always have the information either. Consider the following three ways to invoke a script:

./script

/usr/bin/script

script

In the first and third ways $0 doesn't have the full path information. In the second and third, pwd does not work. The only way to get the directory in the third way would be to run through the path and find the file with the correct match. Basically the code would have to redo what the OS does.

One way to do what you are asking would be to just hardcode the data in the /usr/share directory, and reference it by its full path. Data shoudn't be in the /usr/bin directory anyway, so this is probably the thing to do.

所谓喜欢 2024-07-11 18:42:25

我比较了许多给出的答案,并提出了一些更紧凑的解决方案。 这些似乎可以处理由您最喜欢的组合引起的所有疯狂边缘情况:

  • 绝对路径或相对路径
  • 文件和目录软链接
  • 调用为 scriptbash script、<代码>bash -c 脚本、源脚本。 script
  • 目录和/或文件名中的空格、制表符、换行符、Unicode 等
  • 以连字符开头的文件名

如果您从 Linux 运行,似乎使用 proc 句柄是找到当前运行脚本的完全解析源的最佳解决方案(在交互式会话中,链接指向相应的 /dev/pts/X):

resolved="$(readlink /proc/$/fd/255 && echo X)" && resolved="${resolved%

这有一点丑陋,但修复方法很紧凑且易于理解。 我们不仅仅使用 bash 原语,但我对此没有意见,因为 readlink< /code> 大大简化了任务。 echo X 在变量字符串的末尾添加一个 X,这样文件名中的任何尾随空格就不会被吃掉,并且参数替换 ${行尾的 VAR%X} 去掉了 X。 因为 readlink 添加了自己的换行符(如果不是我们之前的技巧,它通常会在命令替换中被吃掉),所以我们也必须摆脱它。 使用 $'' 引用方案最容易实现这一点,它允许我们使用转义序列,例如 \n 来表示换行符(这也是您可以轻松地制作命名目录和文件)。

上面的内容应该可以满足您在 Linux 上查找当前正在运行的脚本的需求,但是如果您没有可供使用的 proc 文件系统,或者您正在尝试查找某些脚本的完全解析路径,其他文件,那么也许您会发现下面的代码很有帮助。 这只是对上面一行代码的轻微修改。 如果您正在使用奇怪的目录/文件名,使用 lsreadlink 检查输出会提供有用的信息,因为 ls 将输出“simplified " 路径,用 ? 代替换行符之类的内容。

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"
\nX'}"

这有一点丑陋,但修复方法很紧凑且易于理解。 我们不仅仅使用 bash 原语,但我对此没有意见,因为 readlink< /code>大大简化了任务。 echo X 在变量字符串的末尾添加一个 X,这样文件名中的任何尾随空格就不会被吃掉,并且参数替换 ${行尾的 VAR%X} 去掉了 X。 因为 readlink 添加了自己的换行符(如果不是我们之前的技巧,它通常会在命令替换中被吃掉),所以我们也必须摆脱它。 使用 $'' 引用方案最容易实现这一点,它允许我们使用转义序列,例如 \n 来表示换行符(这也是您可以轻松地制作命名目录和文件)。

上面的内容应该可以满足您在 Linux 上查找当前正在运行的脚本的需求,但是如果您没有可供使用的 proc 文件系统,或者您正在尝试查找某些脚本的完全解析路径,其他文件,那么也许您会发现下面的代码很有帮助。 这只是对上面一行代码的轻微修改。 如果您正在使用奇怪的目录/文件名,使用 lsreadlink 检查输出会提供有用的信息,因为 ls 将输出“simplified " 路径,用 ? 代替换行符之类的内容。

I've compared many of the answers given, and came up with some more compact solutions. These seem to handle all of the crazy edge cases that arise from your favorite combination of:

  • Absolute paths or relative paths
  • File and directory soft links
  • Invocation as script, bash script, bash -c script, source script, or . script
  • Spaces, tabs, newlines, Unicode, etc. in directories and/or filename
  • Filenames beginning with a hyphen

If you're running from Linux, it seems that using the proc handle is the best solution to locate the fully resolved source of the currently running script (in an interactive session, the link points to the respective /dev/pts/X):

resolved="$(readlink /proc/$/fd/255 && echo X)" && resolved="${resolved%

This has a small bit of ugliness to it, but the fix is compact and easy to understand. We aren't using bash primitives only, but I'm okay with that because readlink simplifies the task considerably. The echo X adds an X to the end of the variable string so that any trailing whitespace in the filename doesn't get eaten, and the parameter substitution ${VAR%X} at the end of the line gets rid of the X. Because readlink adds a newline of its own (which would normally be eaten in the command substitution if not for our previous trickery), we have to get rid of that, too. This is most easily accomplished using the $'' quoting scheme, which lets us use escape sequences such as \n to represent newlines (this is also how you can easily make deviously named directories and files).

The above should cover your needs for locating the currently running script on Linux, but if you don't have the proc filesystem at your disposal, or if you're trying to locate the fully resolved path of some other file, then maybe you'll find the below code helpful. It's only a slight modification from the above one-liner. If you're playing around with strange directory/filenames, checking the output with both ls and readlink is informative, as ls will output "simplified" paths, substituting ? for things like newlines.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"
\nX'}"

This has a small bit of ugliness to it, but the fix is compact and easy to understand. We aren't using bash primitives only, but I'm okay with that because readlink simplifies the task considerably. The echo X adds an X to the end of the variable string so that any trailing whitespace in the filename doesn't get eaten, and the parameter substitution ${VAR%X} at the end of the line gets rid of the X. Because readlink adds a newline of its own (which would normally be eaten in the command substitution if not for our previous trickery), we have to get rid of that, too. This is most easily accomplished using the $'' quoting scheme, which lets us use escape sequences such as \n to represent newlines (this is also how you can easily make deviously named directories and files).

The above should cover your needs for locating the currently running script on Linux, but if you don't have the proc filesystem at your disposal, or if you're trying to locate the fully resolved path of some other file, then maybe you'll find the below code helpful. It's only a slight modification from the above one-liner. If you're playing around with strange directory/filenames, checking the output with both ls and readlink is informative, as ls will output "simplified" paths, substituting ? for things like newlines.

独闯女儿国 2024-07-11 18:42:25

pwd 可用于查找当前工作目录,dirname 可用于查找特定文件的目录(运行的命令是 $0 ,因此 dirname $0 应该为您提供当前脚本的目录)。

但是,dirname 精确地给出了文件名的目录部分,该部分很可能与当前工作目录相关。 如果您的脚本由于某种原因需要更改目录,那么 dirname 的输出将变得毫无意义。

我建议如下:

#!/usr/bin/env bash

reldir="$( dirname -- "$0"; )";
cd "$reldir";
directory="$( pwd; )";

echo "Directory is ${directory}";

这样,您将获得绝对目录,而不是相对目录。

由于脚本将在单独的 Bash 实例中运行,因此之后无需恢复工作目录,但如果您确实想出于某种原因在脚本中更改回来,您可以轻松分配 的值在更改目录之前将 pwd 更改为变量,以供将来使用。

虽然只是

cd "$( dirname -- "$0"; )";

解决了问题中的具体场景,但我发现拥有更普遍的绝对路径更有用。

pwd can be used to find the current working directory, and dirname to find the directory of a particular file (command that was run, is $0, so dirname $0 should give you the directory of the current script).

However, dirname gives precisely the directory portion of the filename, which more likely than not is going to be relative to the current working directory. If your script needs to change directory for some reason, then the output from dirname becomes meaningless.

I suggest the following:

#!/usr/bin/env bash

reldir="$( dirname -- "$0"; )";
cd "$reldir";
directory="$( pwd; )";

echo "Directory is ${directory}";

This way, you get an absolute, rather than a relative directory.

Since the script will be run in a separate Bash instance, there isn't any need to restore the working directory afterwards, but if you do want to change back in your script for some reason, you can easily assign the value of pwd to a variable before you change directory, for future use.

Although just

cd "$( dirname -- "$0"; )";

solves the specific scenario in the question, I find having the absolute path to more more useful generally.

土豪我们做朋友吧 2024-07-11 18:42:25

这是一个易于记住的脚本:

DIR="$( dirname -- "${BASH_SOURCE[0]}"; )";   # Get the directory name
DIR="$( realpath -e -- "$DIR"; )";    # Resolve its full path if need be

Here is an easy-to-remember script:

DIR="$( dirname -- "${BASH_SOURCE[0]}"; )";   # Get the directory name
DIR="$( realpath -e -- "$DIR"; )";    # Resolve its full path if need be
残月升风 2024-07-11 18:42:25

您可以使用 $BASH_SOURCE

#!/usr/bin/env bash

scriptdir="$( dirname -- "$BASH_SOURCE"; )";

请注意,您需要使用 #!/bin/bash 而不是 #!/bin/sh,因为它是bash 扩展。

You can use $BASH_SOURCE:

#!/usr/bin/env bash

scriptdir="$( dirname -- "$BASH_SOURCE"; )";

Note that you need to use #!/bin/bash and not #!/bin/sh since it's a Bash extension.

独留℉清风醉 2024-07-11 18:42:25

简短回答:

"`dirname -- "$0";`"

或(最好):

"$( dirname -- "$0"; )"

Short answer:

"`dirname -- "$0";`"

or (preferably):

"$( dirname -- "$0"; )"
寂寞清仓 2024-07-11 18:42:25

这应该可以做到:

DIR="$(dirname "$(realpath "$0")")"

这适用于路径中的符号链接和空格。

请参阅 dirnamerealpath 的手册页。

请添加有关如何支持 MacOS 的评论。 抱歉我可以验证一下。

This should do it:

DIR="$(dirname "$(realpath "$0")")"

This works with symlinks and spaces in path.

Please see the man pages for dirname and realpath.

Please add a comment on how to support MacOS. I'm sorry I can verify it.

独自←快乐 2024-07-11 18:42:25
#!/usr/bin/env bash

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

是一个有用的单行代码,无论从何处调用脚本,它都会为您提供脚本的完整目录名称。

只要用于查找脚本的路径的最后一个组成部分不是符号链接(目录链接就可以),它就可以工作。 如果您还想解析脚本本身的任何链接,则需要一个多行解决方案:

#!/usr/bin/env bash

SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
  SOURCE=$(readlink "$SOURCE")
  [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )

最后一个解决方案可以与别名、sourcebash -c 的任意组合一起使用>、符号链接等。

注意:如果在运行此代码段之前 cd 到其他目录,结果可能不正确!

另外,请注意 $CDPATH 陷阱,如果用户巧妙地重写 cd 将输出重定向到 stderr(包括转义序列,例如调用 update_terminal_cwd >&2 时),则 stderr 输出会产生副作用在 Mac 上)。 在 cd 命令末尾添加 >/dev/null 2>&1 将解决这两种可能性。

要了解它是如何工作的,请尝试运行这个更详细的形式:

#!/usr/bin/env bash

SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET=$(readlink "$SOURCE")
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE=$TARGET
  else
    DIR=$( dirname "$SOURCE" )
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE=$DIR/$TARGET # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR=$( dirname "$SOURCE" )
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

它会打印如下内容:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
#!/usr/bin/env bash

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

is a useful one-liner which will give you the full directory name of the script no matter where it is being called from.

It will work as long as the last component of the path used to find the script is not a symlink (directory links are OK). If you also want to resolve any links to the script itself, you need a multi-line solution:

#!/usr/bin/env bash

SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
  SOURCE=$(readlink "$SOURCE")
  [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )

This last one will work with any combination of aliases, source, bash -c, symlinks, etc.

Beware: if you cd to a different directory before running this snippet, the result may be incorrect!

Also, watch out for $CDPATH gotchas, and stderr output side effects if the user has smartly overridden cd to redirect output to stderr instead (including escape sequences, such as when calling update_terminal_cwd >&2 on Mac). Adding >/dev/null 2>&1 at the end of your cd command will take care of both possibilities.

To understand how it works, try running this more verbose form:

#!/usr/bin/env bash

SOURCE=${BASH_SOURCE[0]}
while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET=$(readlink "$SOURCE")
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE=$TARGET
  else
    DIR=$( dirname "$SOURCE" )
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE=$DIR/$TARGET # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR=$( dirname "$SOURCE" )
DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

And it will print something like:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
拥抱影子 2024-07-11 18:42:25

使用dirname "$0"

test.sh:

#!/usr/bin/env bash

echo "The script you are running has:"
echo "basename: [$(basename "$0")]"
echo "dirname : [$(dirname "$0")]"
echo "pwd     : [$(pwd)]"

如果您不从脚本所在的目录运行脚本,单独使用pwd将不起作用。

[~]$ pwd
/home/matt
[~]$ ./test.sh
The script you are running has:
basename: [test.sh]
dirname : [/home/matt]
pwd     : [/home/matt]

[~]$ cd /tmp
[~/tmp]$ ~/test.sh
The script you are running has:
basename: [test.sh]
dirname : [/home/matt]
pwd     : [/tmp]

Use dirname "$0":

test.sh:

#!/usr/bin/env bash

echo "The script you are running has:"
echo "basename: [$(basename "$0")]"
echo "dirname : [$(dirname "$0")]"
echo "pwd     : [$(pwd)]"

Using pwd alone will not work if you are not running the script from the directory it is contained in.

[~]$ pwd
/home/matt
[~]$ ./test.sh
The script you are running has:
basename: [test.sh]
dirname : [/home/matt]
pwd     : [/home/matt]

[~]$ cd /tmp
[~/tmp]$ ~/test.sh
The script you are running has:
basename: [test.sh]
dirname : [/home/matt]
pwd     : [/tmp]
只是我以为 2024-07-11 18:42:25

dirname命令是最基本的,简单解析路径直到$0(脚本名称)变量的文件名:

dirname -- "$0";

但是,正如ma​​tt b指出的那样,返回的路径根据脚本的调用方式而不同。 pwd 不会完成这项工作,因为它只告诉您当前目录是什么,而不是脚本所在的目录。此外,如果执行到脚本的符号链接,您将获取链接所在位置的(可能是相对的)路径,而不是实际的脚本。

其他一些人提到了 readlink 命令,但最简单的是,您可以使用:

dirname -- "$( readlink -f -- "$0"; )";

readlink 将脚本路径解析为从文件系统根目录开始的绝对路径。 因此,任何包含单点或双点、波形符和/或符号链接的路径都将解析为完整路径。

这是一个演示其中每一个的脚本,whatdir.sh

#!/usr/bin/env bash

echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename -- "$0"`"
echo "dirname: `dirname -- "$0"`"
echo "dirname/readlink: $( dirname -- "$( readlink -f -- "$0"; )"; )"

使用相对路径在我的主目录中运行此脚本:

>>>$ ./whatdir.sh
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

再次,但使用脚本的完整路径:

>>>$ /Users/phatblat/whatdir.sh
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

现在更改目录:

>>>$ cd /tmp
>>>$ ~/whatdir.sh
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

最后使用执行脚本的符号链接:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

但是,在一种情况下,当脚本在 bash 中获取(而不是执行)时,这不起作用:

>>>$ cd /tmp
>>>$ . ~/whatdir.sh  
pwd: /tmp
$0: bash
basename: bash
dirname: .
dirname/readlink: /tmp

The dirname command is the most basic, simply parsing the path up to the filename off of the $0 (script name) variable:

dirname -- "$0";

But, as matt b pointed out, the path returned is different depending on how the script is called. pwd doesn't do the job because that only tells you what the current directory is, not what directory the script resides in. Additionally, if a symbolic link to a script is executed, you're going to get a (probably relative) path to where the link resides, not the actual script.

Some others have mentioned the readlink command, but at its simplest, you can use:

dirname -- "$( readlink -f -- "$0"; )";

readlink will resolve the script path to an absolute path from the root of the filesystem. So, any paths containing single or double dots, tildes and/or symbolic links will be resolved to a full path.

Here's a script demonstrating each of these, whatdir.sh:

#!/usr/bin/env bash

echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename -- "$0"`"
echo "dirname: `dirname -- "$0"`"
echo "dirname/readlink: $( dirname -- "$( readlink -f -- "$0"; )"; )"

Running this script in my home dir, using a relative path:

>>>$ ./whatdir.sh
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

Again, but using the full path to the script:

>>>$ /Users/phatblat/whatdir.sh
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Now changing directories:

>>>$ cd /tmp
>>>$ ~/whatdir.sh
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

And finally using a symbolic link to execute the script:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

There is however one case where this doesn't work, when the script is sourced (instead of executed) in bash:

>>>$ cd /tmp
>>>$ . ~/whatdir.sh  
pwd: /tmp
$0: bash
basename: bash
dirname: .
dirname/readlink: /tmp
黒涩兲箜 2024-07-11 18:42:25
pushd . > '/dev/null';
SCRIPT_PATH="${BASH_SOURCE[0]:-$0}";

while [ -h "$SCRIPT_PATH" ];
do
    cd "$( dirname -- "$SCRIPT_PATH"; )";
    SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )";
done

cd "$( dirname -- "$SCRIPT_PATH"; )" > '/dev/null';
SCRIPT_PATH="$( pwd; )";
popd  > '/dev/null';

它适用于所有版本,包括

  • 通过多个深度软链接调用时、
  • 文件)
  • 通过命令“source”(又名 .(点)运算符调用脚本时的
  • 。 当 arg $0 被调用者修改时。
  • “./script”
  • “/full/path/to/script”
  • “/some/path/../../another/path/script”
  • "./some/folder/script"

或者,如果 Bash 脚本本身是一个相对符号链接,您想要跟随它并返回链接到脚本的完整路径:

pushd . > '/dev/null';
SCRIPT_PATH="${BASH_SOURCE[0]:-$0}";

while [ -h "$SCRIPT_PATH" ];
do
    cd "$( dirname -- "$SCRIPT_PATH"; )";
    SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )";
done

cd "$( dirname -- "$SCRIPT_PATH"; )" > '/dev/null';
SCRIPT_PATH="$( pwd; )";
popd  > '/dev/null';

无论如何调用,SCRIPT_PATH 都会以完整路径给出。

只要确保您在脚本的开头找到它即可。

pushd . > '/dev/null';
SCRIPT_PATH="${BASH_SOURCE[0]:-$0}";

while [ -h "$SCRIPT_PATH" ];
do
    cd "$( dirname -- "$SCRIPT_PATH"; )";
    SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )";
done

cd "$( dirname -- "$SCRIPT_PATH"; )" > '/dev/null';
SCRIPT_PATH="$( pwd; )";
popd  > '/dev/null';

It works for all versions, including

  • when called via multiple depth soft link,
  • when the file it
  • when script called by command "source" aka . (dot) operator.
  • when arg $0 is modified from caller.
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

Alternatively, if the Bash script itself is a relative symlink you want to follow it and return the full path of the linked-to script:

pushd . > '/dev/null';
SCRIPT_PATH="${BASH_SOURCE[0]:-$0}";

while [ -h "$SCRIPT_PATH" ];
do
    cd "$( dirname -- "$SCRIPT_PATH"; )";
    SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )";
done

cd "$( dirname -- "$SCRIPT_PATH"; )" > '/dev/null';
SCRIPT_PATH="$( pwd; )";
popd  > '/dev/null';

SCRIPT_PATH is given in full path, no matter how it is called.

Just make sure you locate this at start of the script.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文