我正在编写一个Bash脚本,需要让当前工作目录始终是脚本所在的目录。
默认行为是脚本的当前工作目录与运行脚本的shell一样,但我不想要这种行为。
#!/bin/bash
cd "$(dirname "$0")"
$0
的一部分,则可能会导致某些事物出现问题。例如,在您的脚本中,您可能期望../../
引用位于脚本位置上面两级的目录,但如果涉及符号链接,则不一定如此。 - Rag./script
,那么.
就是正确的目录。切换到.
也会最终进入脚本所在的那个目录,也就是当前工作目录。 - ndimbash script.sh
,那么 $0
的值就是 script.sh
。唯一能让 cd
命令“工作”的方式是因为您不关心失败的命令。如果您使用 set -o errexit
(又称:set -e
)来确保您的脚本不会跳过失败的命令,那么这样将无法工作,因为 cd script.sh
是一个错误。可靠的 [bash 特定] 方法是 cd "$(dirname ${BASH_SOURCE[0]})"
。 - Bruno Bronoskybash script.sh
来执行脚本,这个方法会失败—— $0
只会是文件的名称。 - Chris Watts尝试以下简单的一行命令:
dir="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
注意:在命令中双破折线(--)表示命令选项的结束,因此包含破折号或其他特殊字符的文件不会破坏命令。
注意:在Bash中,使用${BASH_SOURCE[0]}
代替$0
,否则在源代码时路径可能会出现问题(source
/.
)。
*适用于Linux、Mac和其他BSD:
cd "$(dirname "$(realpath -- "$0")")";
注意:默认情况下(如Ubuntu等),在大多数流行的Linux发行版中应该已经安装了realpath
,但在某些发行版中可能会缺少,因此您需要手动安装。
注意:如果您使用Bash,请使用${BASH_SOURCE[0]}
代替$0
,否则在引用(source
/.
)时路径可能会出错。
否则,您可以尝试类似以下的操作(它将使用第一个存在的工具):
cd "$(dirname "$(readlink -f -- "$0" || realpath -- "$0")")"
针对Linux系统:
cd "$(dirname "$(readlink -f -- "$0")")"
*在BSD/Mac上使用GNU readlink:
cd "$(dirname "$(greadlink -f -- "$0")")"
注意:你需要安装 coreutils
(例如,1. 安装Homebrew,2. brew install coreutils
)。
在bash中
在bash中,你可以使用参数展开来实现这个目的,例如:
cd "${0%/*}"
但是如果从同一目录运行脚本,则无法工作。
或者您可以在bash中定义以下函数:
realpath () {
[[ "$1" = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
该函数需要一个参数。如果参数已经是绝对路径,则原样打印它,否则打印$PWD
变量和文件名参数(不包括./
前缀)。
或者这里是来自Debian .bashrc
文件的版本:
function realpath()
{
f=$@
if [ -d "$f" ]; then
base=""
dir="$f"
else
base="/$(basename -- "$f")"
dir="$(dirname -- "$f")"
fi
dir="$(cd -- "$dir" && /bin/pwd)"
echo "$dir$base"
}
相关:
另请参阅:
--
)用于表示命令选项的结束,这样包含破折号或其他特殊字符的文件就不会中断命令。例如尝试通过 touch "-test"
和 touch -- -test
创建文件,然后通过 rm "-test"
和 rm -- -test
删除该文件,看看它们之间的区别。 - kenorbrealpath
包已被弃用,而不是 GNU 的 realpath
。如果您认为不清楚,可以建议进行编辑。 - kenorbcd "$(dirname "$(realpath "$0")")"
这样的例子,是否也应该在参数前使用 --
,像这样 cd "$(dirname -- "$(realpath -- "$0")")"
? - ntninjacd "$(dirname "${BASH_SOURCE[0]}")"
很容易,它有效。
cd "${BASH_SOURCE%/*}" || exit
- caw对于未被符号链接到其他位置(例如$PATH
中)的脚本,接受的答案效果很好。
#!/bin/bash
cd "$(dirname "$0")"
然而,如果脚本通过符号链接运行,
ln -sv ~/project/script.sh ~/bin/;
~/bin/script.sh
这将会进入~/bin/
目录而不是~/project/
目录,如果cd
的目的是相对于~/project/
包含依赖项,那么这可能会破坏您的脚本。
符号链接安全答案如下:
#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" # cd current directory
readlink -f
命令用于解析可能是符号链接的文件的绝对路径。
引号必需用于支持可能包含空格的文件路径(不好的做法,但是不能保证这种情况不存在)。
这里有很多正确的答案,但对我来说更有用的是使用pushd/popd来确保脚本的相对路径始终可预测/可用:
pushd "$(dirname ${BASH_SOURCE:0})"
trap popd EXIT
# ./xyz, etc...
这将把源文件所在的目录推入导航栈中,从而更改工作目录,但是当脚本退出(包括失败)时,trap
将运行 popd
,恢复执行之前的当前工作目录。如果脚本进行了 cd
操作并失败,则执行结束后终端可能处于不可预测的状态 - trap
可以防止这种情况发生。
#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd
pwd命令行会在无论我从哪里运行它,都会将脚本所在的位置作为当前工作目录输出。
realpath
不太可能被安装在所有地方。这可能并不重要,具体取决于提问者的情况。 - bstpierrerealpath
,但是它有 readlink
,看起来很相似。 - Dennis Williamson#!/bin/bash
cd "$(dirname "$0")"
CUR_DIR=$(pwd)
获取脚本的真实路径
if [ -L $0 ] ; then
ME=$(readlink $0)
else
ME=$0
fi
DIR=$(dirname $ME)
(这是对我在此处提出的相同问题的答案:获取脚本执行所在目录的名称)
cd "`dirname $(readlink -f ${0})`"
myscript path/to/file
,我希望该脚本相对于我的当前目录来评估path/to/file
,而不是脚本所在的任何目录。此外,如果使用ssh remotehost bash < ./myscript
运行脚本,会发生什么情况,就像 BASH FAQ 中提到的那样? - Gordon Davissoncd "${BASH_SOURCE%/*}" || exit
的翻译是:进入"${BASH_SOURCE%/*}"目录,如果失败则退出。 - caw