Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fail my Bash script if the current directory doesn't exist?

Tags:

bash

How can I detect in a Bash script if the current directory DOESN'T exist, and fail cleanly with a message like "Your current directory doesn't exist!" ?

So how can I write a script that would work in the following example?

$ mkdir /tmp/wow
$ cd /tmp/wow
$ pwd
/tmp/wow
$ rm -fr /tmp/wow
$ my_script.sh
Your current directory doesn't exist!
like image 331
Brad Parks Avatar asked Jan 04 '23 14:01

Brad Parks


1 Answers

Nonportable Answer

(This depends on standard UNIX filesystem implementation details -- nothing too scary or nonportable there -- but also on the specific implementation of the stat(2) family of syscalls for your filesystem; thus, here there be dragons).

Every inode, including that of directories, retains a "link count" -- the number of references to it. For files, this represents the number of hardlinks; for directories, the link from the parent is 1, the . directory is another, and .. from any subdirectory adds a third or later. (If a directory is /, of course, there is no link from a parent).

With a filesystem properly using this accounting, the stat data will reflect the number of links. Thus:

#!/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

However, not all operating systems correctly expose this accounting. For a HFS filesystem on MacOS, for instance, a directory will not display a link count less than 2 even if it is unlinked from its parents (and thus only existing by virtue of reference-counting in memory -- as discussed below).


Portable Answer

Short form

While it is possible for your current directory to be unlinked (that is, to have no link to it from a parent directory that can be used to traverse to it from the filesystem root), it is impossible for your current directory to not exist. Being your current directory forces a directory to continue to exist, by virtue of keeping its in-memory reference count above zero, even if there exists no parent directory which has it as a subdirectory.

Long form (and buggy, broken workaround)

If you're willing to have false-positives when a directory has been moved:

#!/bin/sh
[ -d "$PWD" ] || { echo "Your current directory doesn't exist!" >&2; exit 1; }

That said, this doesn't detect the case when a new and different directory has been created where your old one used to be. To handle that, we might add some additional logic, like the following:

# 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

However, like any other file, having an open handle on a directory will prevent it from being deleted as such. Thus, rm -rf /tmp/wow in your example isn't really deleting the directory, until your shell (and any other software) is no longer in it, because until that happens, its in-memory reference count isn't 0.

Consequently, when [ -d . ] tells you that your directory still exists, even though it's been deleted... that's actually true! Your directory does still exist, because the fact that your shell is currently in it forces it to continue to exist.

like image 196
Charles Duffy Avatar answered Jan 13 '23 13:01

Charles Duffy