November 19th 2009

Transparent directory replacement with zsh

Under UNIX, it is possible to get into a position where your shell's working directory is pointing to an inode that no longer exists, but a "newer" directory with the same name exists.

The easiest way of reproducing this scenario is as follows:

$ cd $(mktemp -d)
$ rm -rf $PWD
$ mkdir $PWD

However, as a concrete example, imagine you are debugging something deep inside a generated code tree — if you ran make clean and then make again from another shell, your debugging shell would be left pointing to an inode that does not exist (as it was removed by make clean), yet the make call would have made an identically named directory in its place.

Shells behave rather unintuitively in this situation; ls will (correctly) return no results, but attempting to do anything useful results in curious error messages.

I use the following zsh precmd hook to transparently fix this for me:

precmd () {
  # "DOS emulation mode"
  if [ "$(stat -c %i . 2>/dev/null)" != \
                 "$(stat -c %i -- "${PWD}" 2>/dev/null)" ]
  then
    OLDOLDPWD="${OLDPWD}"
    if ! cd -- "${PWD}" >/dev/null 2>&1
    then
      echo "W: ${PWD} does not exist anymore"
      return 1
    fi
    OLDPWD="${OLDOLDPWD}"
  fi
}

The cost of the stat calls seem justified by the convenience and are in any case insignificant compared to the other things I am doing in my precmd. If you are using zmodload zsh/stat, then replace stat -c %i with stat +inode.

(My previous experience with zsh tricks is that the functionality invariably exists already in some obscurely named builtin. Here's hoping.)




You can subscribe to new posts via email or RSS.