Bash 如果当前目录没有';不存在?

Bash 如果当前目录没有';不存在?,bash,Bash,我如何在Bash脚本中检测当前目录是否不存在,并以“您的当前目录不存在!”这样的消息彻底失败 那么,我如何编写一个在下面的示例中工作的脚本呢 $ mkdir /tmp/wow $ cd /tmp/wow $ pwd /tmp/wow $ rm -fr /tmp/wow $ my_script.sh Your current directory doesn't exist! 不可移植答案 (这取决于标准的UNIX文件系统实现细节——没有什么太可怕或不可移植的地方——还取决于文件系统的stat(2

我如何在Bash脚本中检测当前目录是否不存在,并以“您的当前目录不存在!”这样的消息彻底失败

那么,我如何编写一个在下面的示例中工作的脚本呢

$ mkdir /tmp/wow
$ cd /tmp/wow
$ pwd
/tmp/wow
$ rm -fr /tmp/wow
$ my_script.sh
Your current directory doesn't exist!
不可移植答案 (这取决于标准的UNIX文件系统实现细节——没有什么太可怕或不可移植的地方——还取决于文件系统的
stat(2)
系统调用系列的具体实现;因此,这里有龙)

每个inode,包括目录的inode,都保留一个“链接计数”——对它的引用数。对于文件,这表示硬链接的数量;对于目录,来自父目录的链接是1,
目录是另一个,来自任何子目录的
添加第三个或更高版本。(如果目录是
/
,当然,没有来自父目录的链接)

如果文件系统正确地使用这种记帐,stat数据将反映链接的数量。因此:

#!/bin/bash
link_count=$(stat --format=%h .)    ## --format=%h requires GNU stat
if (( link_count == 0 )); then      ## 0 works w/ ext* on Linux, not guaranteed elsewhere
  echo "This directory has been deleted! Voluntarily exiting" >&2
  exit 1
fi
但是,并非所有操作系统都正确地公开了这种记帐。例如,对于MacOS上的HFS文件系统,即使目录与其父目录断开链接(因此仅通过内存中的引用计数存在,如下所述),目录也不会显示小于2的链接计数


便携式回答 缩略形式 虽然可以取消当前目录的链接(也就是说,没有从父目录到当前目录的链接,而父目录可以用来从文件系统根目录遍历到当前目录),但当前目录不可能不存在。作为当前目录,即使不存在将目录作为子目录的父目录,也会使其内存引用计数保持在零以上,从而强制目录继续存在

长形(以及多车、破损的工作区) 如果您希望在移动目录时出现误报:

这就是说,当一个新的和不同的目录被创建时,旧的目录就被创建了,这并没有检测到这种情况。为了处理这个问题,我们可以添加一些额外的逻辑,如下所示:

# perl isn't *truly* portable, in the POSIX-specified sense, but it's everywhere.
ino() { perl -e '@s=stat($ARGV[0]); printf("%s %s\n", $s[0], $s[1]);' "$@"; }
cur_ino=$(ino .)
pwd_ino=$(ino "$PWD")
if [ "$cur_ino" != "$pwd_ino" ]; then
  echo "Directory has been replaced! Following..." >&2
  cd "$PWD" || exit
fi
但是,与任何其他文件一样,在目录上有一个打开的句柄将防止它被删除。因此,在您的示例中,
rm-rf/tmp/wow
并没有真正删除目录,直到您的shell(和任何其他软件)不再在其中,因为在此之前,它的内存引用计数不是0


因此,当
[-d.]
告诉您您的目录仍然存在时,即使它已被删除。。。这是真的!您的目录仍然存在,因为您的shell当前在其中,这一事实迫使它继续存在。

通常一个测试目录(任何目录,包括当前工作目录)是否存在只是为了方便:

  • 创建缺少的目录(
    mkdir-p
  • 存储文件(
    mktemp
    touch
    等)
  • 最后一种情况可能可用于检测当前工作目录(
    )是否可用:

    function cwd_exists() {
        local fn
        local rc
        fn=$(TMPDIR=. mktemp 2>/dev/null)
        rc=$?
        [ -e "$fn" ] && rm -f "$fn"
        return $rc;
    }
    
    在演示脚本中插入:

    home=$(mktemp -d)
    cd "$home"
    cwd_exists && echo 'Exists' || echo 'Gone'
    mv "$home" "$home.backup"
    cwd_exists && echo 'Exists' || echo 'Gone'
    mv "$home.backup" "$home"
    cwd_exists && echo 'Exists' || echo 'Gone'
    rm -rf "$home"
    cwd_exists && echo 'Exists' || echo 'Gone'
    mkdir "$home"
    cwd_exists && echo 'Exists' || echo 'Gone'
    
    将响应我认为是期望的行为:

    Exists
    Exists
    Exists
    Gone
    Gone
    

    即使目录存在(由于脚本保持打开状态),当
    mktemp
    尝试写入目录时,该目录也不存在,即使出现另一个进程并给出相同的路径名(因为索引节点不同)。

    如果要检查是否删除了当前目录,请尝试以下操作:

    # perl isn't *truly* portable, in the POSIX-specified sense, but it's everywhere.
    ino() { perl -e '@s=stat($ARGV[0]); printf("%s %s\n", $s[0], $s[1]);' "$@"; }
    cur_ino=$(ino .)
    pwd_ino=$(ino "$PWD")
    if [ "$cur_ino" != "$pwd_ino" ]; then
      echo "Directory has been replaced! Following..." >&2
      cd "$PWD" || exit
    fi
    
    #首先获取当前目录的inode编号,例如117002
    ls-ial | grep'\s\.$| awk'{print$1}'
    #然后尝试在父目录中查找inode的路径。如果没有,则删除原始目录
    找到-因努姆117002
    
    警告:如果您需要处理重命名,那么您就有问题了。在目录中,它本身增加了它的引用计数,因此意味着它没有被真正删除,即使它不是从任何父目录中都无法访问的。我假设
    my_script.sh
    实际上生活在
    /tmp/wow
    ?@chepner-是的,它生活在我的
    ~/bin/
    文件夹中…实际上——有一个更好的答案,但它是特定于平台的。我们只能在这里玩Linux吗?嘿!谢谢你的想法!我已经试过了,但还是失败了。这是我尝试运行您建议的
    shell init:error检索当前目录:getcwd:无法访问父目录:没有这样的文件或目录您的当前目录不存在是的--这些消息是在安装过程中在脚本实际启动之前生成的,因此您对此无能为力。@BradParks,…也就是说,除了最后的
    之外,您当前的目录不存在,这些消息只是stderr上的警告——它们实际上并没有修改行为或脚本输出(在正式意义上是“写入stdout的内容”)。退出状态是我的命令中的
    exit
    给出的状态。如果您尝试将
    退出1
    修改为,比如说,
    退出5
    ,并在运行脚本后选中
    $?
    ,您会发现情况确实如此。Hmmm。。。我明白了。。。。如果我只是编辑我的脚本,只需
    echo hi
    ,这就行了,但只要我尝试在脚本中使用
    $PWD
    PWD
    ,它就会像上面那样消失。如果有一个棘手的方法来找到PWD,那么我可能可以对它做一个不同的检查…再一次,它“像上面一样消亡”,因为我给你的代码使它消亡,因为这是我认为你想要的<代码>echo$PWD
    本身会向stderr发出一些警告,但它仍然会打印上次存在的工作目录,并且不会导致任何类型的死亡。啊——编辑后,这不再容易出现问题(您的
    mv“$home”“$home.backup”
    案例涵盖了它)。(这里有一个警告,“现有但不可写”可以混为一谈