【问题标题】:How can I set the current working directory to the directory of the script in Bash?如何将当前工作目录设置为 Bash 中脚本的目录?
【发布时间】:2011-03-21 22:00:43
【问题描述】:

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

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

【问题讨论】:

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

标签: bash path scripting


【解决方案1】:

这个脚本似乎对我有用:

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

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

【讨论】:

  • realpath 不可能到处安装。这可能无关紧要,具体取决于 OP 的情况。
  • 我的系统没有realpath,但它确实有readlink,看起来很相似。
  • 哪个dist默认没有安装realpath? Ubuntu 有它。
【解决方案2】:
#!/bin/bash
cd "$(dirname "$0")"

【讨论】:

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

获取脚本的真实路径

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

【讨论】:

    【解决方案4】:
    cd "$(dirname "${BASH_SOURCE[0]}")"
    

    这很容易。它有效。

    【讨论】:

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

    以下也有效:

    cd "${0%/*}"
    

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

    【讨论】:

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

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

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

    赋予执行权限:

    chmod u+x test
    

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

    【讨论】:

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

    尝试以下简单的单行语句:


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

    相关:

    另见:

    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。如果您认为不清楚,可以提出修改建议。
    【解决方案8】:
    cd "`dirname $(readlink -f ${0})`"
    

    【讨论】:

    • 你能解释一下你的答案吗?
    • 虽然这段代码可能有助于解决问题,但它并没有解释为什么和/或如何回答问题。提供这种额外的背景将显着提高其长期教育价值。请edit您的答案添加解释,包括适用的限制和假设。
    【解决方案9】:
    echo $PWD
    

    PWD 是一个环境变量。

    【讨论】:

    • 这只会给出调用的目录,而不是脚本所在的目录。
    【解决方案10】:

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

    引号是支持可能包含空格的文件路径所必需的(不好的做法,但假设不会出现这种情况是不安全的)

    【讨论】:

      【解决方案11】:

      我接受了它,它有效。

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

      【讨论】:

        【解决方案12】:

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

        【讨论】:

          【解决方案13】:

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

          pushd "$(dirname ${BASH_SOURCE:0})"
          trap popd EXIT
          
          # ./xyz, etc...
          

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

          【讨论】:

            猜你喜欢
            • 2011-07-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-21
            相关资源
            最近更新 更多