Reputation: 71991
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!
Upvotes: 4
Views: 2148
Reputation: 721
If you want to check whether current dir is deleted, try following:
# First get the inode number of current dir, e.g. 117002
ls -ial | grep '\s\.$' | awk '{print $1}'
# Then try to find the inode's path in parent dir. If none, then the original dir was deleted
find .. -inum 117002
Upvotes: 1
Reputation: 39394
Typically one tests if a directory (any directory, including the current working one) exists only to facilitate either:
mkdir -p
)mktemp
, touch
, etc.)It is this last case that can, perhaps, be used to detect if your current working directory (.
) is available:
function cwd_exists() {
local fn
local rc
fn=$(TMPDIR=. mktemp 2>/dev/null)
rc=$?
[ -e "$fn" ] && rm -f "$fn"
return $rc;
}
Inserting into a demo script:
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'
Will echo what I think is the desired behavior:
Exists
Exists
Exists
Gone
Gone
Even though the directory exists (by virtue of your script holding it open), the directory doesn't exist when mktemp
tries to write into it, even if another process comes along and gives the same path name (because the inodes are different).
Upvotes: 2
Reputation: 295403
(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).
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.
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.
Upvotes: 6