我如何找到一个Bash脚本位于该脚本内部的目录的路径?
我想用Bash脚本作为另一个应用程序的启动器,我想将工作目录更改为Bash脚本所在的目录,所以我可以在该目录中的文件上运行,如下:
$ ./application
我如何找到一个Bash脚本位于该脚本内部的目录的路径?
我想用Bash脚本作为另一个应用程序的启动器,我想将工作目录更改为Bash脚本所在的目录,所以我可以在该目录中的文件上运行,如下:
$ ./application
当前回答
下面是一个命令,在Bash或zsh下工作,以及是否单独执行或源:
[ -n "$ZSH_VERSION" ] && this_dir=$(dirname "${(%):-%x}") \
|| this_dir=$(dirname "${BASH_SOURCE[0]:-$0}")
它是如何工作的
Fallback 替代操作员:
% x=ok
% echo "${x}"
ok
% echo "${x:-fallback}"
ok
% x=
% echo "${x:-fallback}"
fallback
% y=yvalue
% echo "${x:-$y}"
yvalue
此分類上一篇: %x prompt escape code
接下来,我们将引入即时逃避代码,一个 zsh-only 功能. 在 zsh 中, %x 将扩展到文件的路径,但通常只有在进行即时行扩展时。
% cat apath/test.sh
fpath=%x
echo "${(%)fpath}"
% source apath/test.sh
apath/test.sh
% cd apath
% source test.sh
test.sh
% cat test.sh
echo "${(%):-%x}"
% source test.sh
test.sh
请注意,我们通常会把变量名称放在(%)和(%)之间,但我们留下了白色。
我们可能已经使用打印 -P %x 以获得相同的文件路径,有更少的黑客,但在我们的情况下,我们需要将其作为一个论点转到一个字符串,这将需要一个开始一个新的字符串的顶部:
% cat apath/test.sh
dirname "$(print -P %x)" # $(...) runs a command in a new process
dirname "${(%):-%x}"
% source apath/test.sh
apath
apath
显然,黑客的方式既更有效又更简短。
其他回答
在完整的披露中,......(我找到了这个使者的变量部分)以及在Rich’s sh的技巧中,我也在我自己的答案的披露下披露了他的页面的相关部分。
具体的:
雖然不是嚴格 POSIX 到目前為止, realpath 是一個 GNU 核心應用程式自 2012 年 全公開:我從未聽到它之前,我注意到它在 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
另一方面,你可以这样做:
ps ww -fp $$ | grep -Eo '/[^:]*'"${0#*/}"
eval "abs0=${`ps ww -fp $$ | grep -Eo ' /'`#?}"
试试这:
func () {
body here
eval "$1=\${foo}"
}
foo='hello ; rm -rf /'
dest=bar
eval "$dest=$foo"
当然,下面的版本是完全安全的:
foo='hello ; rm -rf /'
dest=bar
eval "$dest=\$foo"
这种方法的一个好处是,它不涉及Bash本身以外的任何东西,也不会破坏任何底层。
首先,使用模式替代替代不以 / (即相对路径) 以 $ PWD/. 因为我们使用替代替代相匹配的第一个字符为 $ 0,我们也必须添加它回来(${0:0:1} 在替代中)。
现在我们有一个完整的路径到脚本;我们可以通过删除最后的 / 和随后的任何东西(即脚本名称)来获得目录。
#!/bin/bash
BIN=${0/#[!\/]/"$PWD/${0:0:1}"}
DIR=${BIN%/*}
cd "$DIR"
如果您的脚本可以源于而不是执行,您可以将 $0 取代 ${BASH_SOURCE[0]},例如:
BIN=${BASH_SOURCE[0]/#[!\/]/"$PWD/${BASH_SOURCE[0]:0:1}"}
这将为可执行的脚本工作,它更长,但更多。
总结:
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")"
细节:
在很多情况下,所有你需要获得的是你刚刚打电话的脚本的完整路径. 这可以很容易地通过 realpath 实现. 请注意, realpath 是 GNU 核心工具的一部分. 如果你没有它已经安装(它是默认的在 Ubuntu 上),你可以安装它与 sudo apt 更新 && sudo apt 安装核心工具。
#!/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\""
如果您在脚本中使用“$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"
路径与路径之间的区别:
请注意,直路也成功地走下象征性链接来确定并指向他们的目标,而不是指向象征性链接。 如果你不想要这种行为(有时我不),然后添加到上面的直路命令,使该线看起来像这样:
# 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]}")"
参考:
[我的答案] Unix 和 Linux:确定路径到源头 Shell 脚本
这对我来说工作了,当这里的其他答案没有:
thisScriptPath=`realpath $0`
thisDirPath=`dirname $thisScriptPath`
echo $thisDirPath
我认为最简单的答案是原始变量的参数扩展:
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
echo "opt1; original answer: $DIR"
echo ''
echo "opt2; simple answer : ${BASH_SOURCE[0]%/*}"
它应该产生产量如:
$ /var/tmp/test.sh
opt1; original answer: /var/tmp
opt2; simple answer : /var/tmp
变量/参数扩展 ${BASH_SOURCE[0]%/*}”似乎更容易保持。