我正在写一个 Bash 脚本。我需要当前工作目录始终是脚本所在的目录。
默认行为是脚本中的当前工作目录是我运行它的 shell 的目录,但我不希望这种行为。
myscript path/to/file
,我希望脚本评估相对于我当前目录的路径/到/文件,而不是任何目录该脚本恰好位于其中。此外,对于使用 ssh remotehost bash < ./myscript
运行的脚本,如 BASH 常见问题解答所述,您会发生什么?
cd "${BASH_SOURCE%/*}" || exit
#!/bin/bash
cd "$(dirname "$0")"
以下也有效:
cd "${0%/*}"
this StackOverflow 答案中详细描述了语法。
bash script.sh
调用脚本,则会失败 - $0
将只是文件的名称
尝试以下简单的单行:
适用于所有 UNIX/OSX/Linux
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")")";
注意:realpath
应默认安装在最流行的 Linux 发行版(如 Ubuntu)中,但在某些情况下可能会丢失,因此您必须安装它。
注意:如果您使用的是 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 中,您可以使用 Parameter Expansions 来实现这一点,例如:
cd "${0%/*}"
但如果脚本从同一目录运行,则它不起作用。
或者,您可以在 bash 中定义以下函数:
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
这个函数有 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"
}
有关的:
如何检测我运行 shell 脚本的当前目录?
从脚本本身中获取 Bash 脚本的源目录
带有 OS X 的 Bash 脚本绝对路径
Bash 脚本获取自身完整路径的可靠方法
也可以看看:
How can I get the behavior of GNU's readlink -f on a Mac?
cp
命令中的作用是什么,@kenorb?
--
) 来表示命令选项的结束,因此包含破折号或其他特殊字符的文件不会中断命令。尝试例如通过 touch "-test"
和 touch -- -test
创建文件,然后通过 rm "-test"
和 rm -- -test
删除文件,看看有什么不同。
realpath
包被弃用,而不是 GNU realpath
。如果您认为不清楚,可以提出修改建议。
cd "$(dirname "${BASH_SOURCE[0]}")"
这很简单。有用。
cd "${BASH_SOURCE%/*}" || exit
接受的答案适用于未在其他地方进行符号链接的脚本,例如 $PATH
。
#!/bin/bash
cd "$(dirname "$0")"
但是,如果脚本是通过符号链接运行的,
ln -sv ~/project/script.sh ~/bin/;
~/bin/script.sh
这将 cd 到 ~/bin/
目录而不是 ~/project/
目录,如果 cd
的目的是包含相对于 ~/project/
的依赖项,这可能会破坏您的脚本
符号链接安全答案如下:
#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" # cd current directory
readlink -f
是解析潜在符号链接文件的绝对路径所必需的。
需要引号来支持可能包含空格的文件路径(不好的做法,但假设情况并非如此是不安全的)
这个脚本似乎对我有用:
#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd
pwd 命令行将脚本的位置回显为当前工作目录,无论我从哪里运行它。
realpath
不太可能安装在任何地方。这可能无关紧要,具体取决于 OP 的情况。
realpath
,但确实有readlink
,看起来很相似。
这里有很多正确的答案,但对我更有用的一个(确保脚本的相对路径保持可预测/有效)是使用 pushd/popd:
pushd "$(dirname ${BASH_SOURCE:0})"
trap popd EXIT
# ./xyz, etc...
这会将源文件的目录推送到导航堆栈,从而更改工作目录,但是,当脚本退出时(无论出于何种原因,包括失败),trap
将运行 popd
,恢复当前工作目录在被执行之前。如果脚本执行 cd
然后失败,则在执行结束后您的终端可能会处于不可预知的状态 - 陷阱会阻止这种情况。
我接受这个并且它有效。
#!/bin/bash
cd "$(dirname "$0")"
CUR_DIR=$(pwd)
获取脚本的真实路径
if [ -L $0 ] ; then
ME=$(readlink $0)
else
ME=$0
fi
DIR=$(dirname $ME)
(这是我的问题的答案:Get the name of the directory where a script is executed)
cd "`dirname $(readlink -f ${0})`"
大多数答案要么不处理通过相对路径符号链接的文件,要么不处理单行文件,要么不处理 BSD (Mac)。一个解决所有三个问题的解决方案是:
HERE=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SOURCE" || echo .)")"; pwd)
首先, cd 到 bash 脚本目录的概念。然后读取文件以查看它是否是符号链接(相对或其他),如果是,则 cd 到该目录。如果没有,则 cd 到当前目录(必须保持单线)。然后通过 pwd
回显当前目录。
您可以将 --
添加到 cd 和 readlink 的参数中,以避免出现类似选项的目录问题,但我不会为大多数目的而烦恼。
您可以在此处查看带有插图的完整说明:
https://www.binaryphile.com/bash/2020/01/12/determining-the-location-of-your-script-in-bash.html
echo $PWD
PWD 是一个环境变量。
如果您只需要打印当前的工作目录,那么您可以按照此操作。
$ vim test
#!/bin/bash
pwd
:wq to save the test file.
授予执行权限:
chmod u+x test
然后通过./test
执行脚本就可以看到当前的工作目录了。
pwd
?对我来说,这似乎是很多浪费的按键。
不定期副业成功案例分享
$0
的一部分,事情可能会中断。例如,在您的脚本中,您可能期望../../
指代脚本位置上方两级的目录,但如果符号链接正在运行,则不一定是这种情况。./script
,则.
是正确的目录,更改为.
后,它也会出现在script
所在的目录中,即当前工作目录中。bash script.sh
那样从当前目录运行脚本,则$0
的值为script.sh
。cd
命令为您“工作”的唯一方法是因为您不关心失败的命令。如果您要使用set -o errexit
(又名:set -e
)来确保您的脚本不会跳过失败的命令,那么这将不起作用,因为cd script.sh
是一个错误。可靠的 [bash 特定] 方式是cd "$(dirname ${BASH_SOURCE[0]})"