Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

How can I check that a given regular file of path f is a descendant of a directory of path d. If not for symlinks, I would just check if d (with a trailing slash) is prefix to f. Now with symlinks this gets marginally more complicated, as I could find the physical paths of the ancestors of f and compare each of them to the physical path of d. However, I believe there might be a userland utility for that, is there one?

share|improve this question

2 Answers 2

up vote 3 down vote accepted
  1. If the ancestors of a file are defined as the directory containing it and the ancestors of that directory, i.e., the descendants of a directory are its entries and (for the entries that are directories) their own descendants:

    Assuming that $f is the path of a regular file (and not the path of a symbolic link), you can find the “real” directory containing it with $(cd -- "$(dirname "$f")" && command -p pwd). Similarly, find the “real” location of the directory $d with $(cd -- "$d" && command -p pwd). In bash/ksh/zsh, you can replace command -p pwd by pwd -P.

    If $f may be a symbolic link, it's more complicated. Some systems have a readlink command, for example you could use $(dirname "$(readlink -f -- "$f")) with reasonably recent GNU coreutils, but note that there are other systems with an incompatible readlink command or none at all.

    Note that the file may be moved between the time you make the check and the time you use the result. There are also corner cases where the question doesn't have a clear yes/no answer, such as when the path from the directory to the file traverses a mount point.

    If all you want to know is which filesystem $f is on, run df "$f".

  2. If the descendants of a directory $d are defined as the files that can be given a name of the form $d/x1/x2/.../xn where the xi can be directories or symbolic links:

    Testing this property requires a traversal of the whole directory tree rooted at $d, following all symbolic links. The following command should do this (warning, typed directly into the browser; requires GNU find):

    [ -n "$(find -L "$d" -samefile "$f" -print -quit)" ]
    

    If $f has hard links, this may access the file $f through a different name. If you require the file to be found under the name $f, it gets more complicated; here's an attempt (again, typed directly into the browser; requires GNU find and GNU grep; assumes no newlines in file names):

    f_directory="$(cd -- "$(dirname "$f")" && command -p pwd)"
    f_truename="$f_directory/$(basename "$f")"
    [ -n "$(find -L "$(cd "$d" && command -p pwd)" -exec readlink -f \; |
            grep -F -q -x "$f_truename")" ]
    

    No matter what exact definition you choose, this property is not very robust since you can change its value by adding or removing a symbolic link deep underneath $d.

share|improve this answer
    
Does this account for symlinks to directories? Let's assume my file is /x/f and I have a symlink /y/sx to /x. I can get to the file from /y, despite /y/ not being prefix to /x/f. –  Adrian Panasiuk Sep 1 '10 at 12:42
    
@Adrian: I'd understood your question to require that /x/f be considered not underneath /y in this case. What you're asking here is a very different property; I've added a way to test it to my answer. –  Gilles Sep 1 '10 at 18:31

My 'ls' command has flag "--dereference-command-line-symlink-to-dir" that shows the full path to the file. This 'ls' is from GNU coreutils 6.9.

share|improve this answer
1  
If you have that, you also have the readlink from coreutils, which can show the same information in a machine-parsable way. (Don't parse the output of ls.) –  Gilles Sep 1 '10 at 0:18

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.