ChatGPT解决这个技术问题 Extra ChatGPT

如何将当前工作目录设置为 Bash 中脚本的目录?

我正在写一个 Bash 脚本。我需要当前工作目录始终是脚本所在的目录。

默认行为是脚本中的当前工作目录是我运行它的 shell 的目录,但我不希望这种行为。

您是否考虑过将包装脚本放在 /usr/bin 之类的地方以 cd 进入(硬编码的)正确目录,然后执行您的脚本?
为什么需要脚本的目录?可能有更好的方法来解决根本问题。
我只想指出,您所说的“明显不受欢迎”的行为实际上是完全必要的——如果我运行 myscript path/to/file,我希望脚本评估相对于我当前目录的路径/到/文件,而不是任何目录该脚本恰好位于其中。此外,对于使用 ssh remotehost bash < ./myscript 运行的脚本,如 BASH 常见问题解答所述,您会发生什么?
cd "${BASH_SOURCE%/*}" || exit

n
ndim
#!/bin/bash
cd "$(dirname "$0")"

目录名返回 '.'在 Windows 下使用 bash 时。所以,保罗的回答更好。
也返回 '.'在 Mac OS X 中
值得注意的是,如果符号链接构成 $0 的一部分,事情可能会中断。例如,在您的脚本中,您可能期望 ../../ 指代脚本位置上方两级的目录,但如果符号链接正在运行,则不一定是这种情况。
如果您将脚本称为 ./script,则 . 是正确的目录,更改为 . 后,它也会出现在 script 所在的目录中,即当前工作目录中。
如果您像 bash script.sh 那样从当前目录运行脚本,则 $0 的值为 script.shcd 命令为您“工作”的唯一方法是因为您不关心失败的命令。如果您要使用 set -o errexit(又名:set -e)来确保您的脚本不会跳过失败的命令,那么这将不起作用,因为 cd script.sh 是一个错误。可靠的 [bash 特定] 方式是 cd "$(dirname ${BASH_SOURCE[0]})"
R
Richard

以下也有效:

cd "${0%/*}"

this StackOverflow 答案中详细描述了语法。


解释它是如何工作的:stackoverflow.com/questions/6393551/…
如果路径包含空格,请不要忘记用引号引起来。即 cd "${0%/*}"
如果使用 bash script.sh 调用脚本,则会失败 - $0 将只是文件的名称
如果脚本在根目录中,它也不起作用,因为“${0%/*}”会扩展为空字符串
我想说这个答案太简洁了。你几乎没有学到任何关于语法的东西。关于如何阅读本文的一些描述会有所帮助。
k
kenorb

尝试以下简单的单行:

适用于所有 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?


一个比流行的更好的答案,因为它解决了系统链接,并考虑了不同的操作系统。谢谢!
感谢您的回答。最上面的那个在我的 Mac 上工作...但是 -- 开关在 cp 命令中的作用是什么,@kenorb?
命令中使用双破折号 (--) 来表示命令选项的结束,因此包含破折号或其他特殊字符的文件不会中断命令。尝试例如通过 touch "-test"touch -- -test 创建文件,然后通过 rm "-test"rm -- -test 删除文件,看看有什么不同。
如果可以的话,我会删除我的投票:不推荐使用 realpath unix.stackexchange.com/questions/136494/…
@lsh 它说只有 Debian realpath 包被弃用,而不是 GNU realpath。如果您认为不清楚,可以提出修改建议。
J
John Kugelman
cd "$(dirname "${BASH_SOURCE[0]}")"

这很简单。有用。


这应该是公认的答案。适用于 OSX 和 Linux Ubuntu。
当脚本 B 来自其他脚本 A 并且您需要使用相对于脚本 B 的路径时,这非常有用。谢谢。
对于我们这些不幸不得不触摸 Windows 的人来说,这也适用于 Cygwin。
cd "${BASH_SOURCE%/*}" || exit
适用于 macOS Mojave
J
James McGuigan

接受的答案适用于未在其他地方进行符号链接的脚本,例如 $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 是解析潜在符号链接文件的绝对路径所必需的。

需要引号来支持可能包含空格的文件路径(不好的做法,但假设情况并非如此是不安全的)


A
Amardeep AC9MF

这个脚本似乎对我有用:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

pwd 命令行将脚本的位置回显为当前工作目录,无论我从哪里运行它。


realpath 不太可能安装在任何地方。这可能无关紧要,具体取决于 OP 的情况。
我的系统没有realpath,但确实有readlink,看起来很相似。
哪个 dist 默认没有安装 realpath? Ubuntu 有它。
A
Andrew Theken

这里有很多正确的答案,但对我更有用的一个(确保脚本的相对路径保持可预测/有效)是使用 pushd/popd:

pushd "$(dirname ${BASH_SOURCE:0})"
trap popd EXIT

# ./xyz, etc...

这会将源文件的目录推送到导航堆栈,从而更改工作目录,但是,当脚本退出时(无论出于何种原因,包括失败),trap 将运行 popd,恢复当前工作目录在被执行之前。如果脚本执行 cd 然后失败,则在执行结束后您的终端可能会处于不可预知的状态 - 陷阱会阻止这种情况。


R
Robert Hu

我接受这个并且它有效。

#!/bin/bash
cd "$(dirname "$0")"
CUR_DIR=$(pwd)

C
Community

获取脚本的真实路径

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})`"

你能解释一下你的答案吗?
尽管此代码可能有助于解决问题,但它并没有解释 为什么 和/或 如何 回答问题。提供这种额外的背景将显着提高其长期教育价值。请edit您的回答以添加解释,包括适用的限制和假设。
${0} 将给出脚本的文件名。 readlink -f {0} 将给出该脚本的绝对路径。 dirname 将从先前的结果中提取脚本所在的路径。
B
Binary Phile

大多数答案要么不处理通过相对路径符号链接的文件,要么不处理单行文件,要么不处理 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


J
Julian
echo $PWD

PWD 是一个环境变量。


这只会给出调用的目录,而不是脚本所在的目录。
K
Keith Smiley

如果您只需要打印当前的工作目录,那么您可以按照此操作。

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

授予执行权限:

chmod u+x test

然后通过./test执行脚本就可以看到当前的工作目录了。


问题是如何确保脚本在其 自己的 目录中运行,包括是否不是 工作目录。所以这不是一个答案。无论如何,为什么要这样做而不是仅仅...运行 pwd?对我来说,这似乎是很多浪费的按键。