I don't think there's such an utility. With GNU readlink, you could do something like:
is_in() (
needle=$(readlink -ve -- "$1" && echo .) || exit
haystack=$(readlink -ve -- "$2" && echo .) || exit
needle=${needle%??} haystack=${haystack%??}
haystack=${haystack%/} needle=${needle%/}
case $needle in
("$haystack" | "$haystack"/*) true;;
(*) false;;
esac
)
That resolves all symlinks to end up with a canonical absolute path for both needle and haystack.
Explanation
We get the canonical absolute path of both the needle and the haystack. We use -e instead of -f as we want to make sure the files exist. The -v option gives an error message if the files can't be accessed.
As always, -- should be used to mark the end of options and quoting as we don't want to invoke the split+glob operator here.
Command substitution in Bourne-like shells have a misfeature in that it removes all the newline character from the end the output of a command, not just the one added by commands to end the last line. What that means is that for a file like /foo<LF><LF>, $(readlink -ve -- "$1") would return /foo. The common work-around for that is to append a non-LF character (here .) and strip that and the extra LF character added by readlink with var=${var%??} (remove the last two characters).
The needle is regarded as being in the haystack if it is the haystack or if it is haystack/something. However, that wouldn't work if the haystack was / (/etc for instance is not //something). / often needs to be treated specially because while / and /xx have the same number of slashes, one is a level above the other.
One way to address it is to replace / with the empty string which is done with var=${var%/} (the only path ending with / that readlink -e may output is /, so removing a trailing / is changing / to the empty string).
For the canonizing of the file paths, you could use a helper function.
canonicalize_path() {
# canonicalize paths stored in supplied variables. `/` is returned as
# the empty string.
for _var do
eval '
'"$_var"'=$(readlink -ve -- "${'"$_var"'}" && echo .) &&
'"$_var"'=${'"$_var"'%??} &&
'"$_var"'=${'"$_var"'%/}' || return
done
}
is_in() (
needle=$1 haystack=$2
canonicalize_path needle haystack || exit
case $needle in
("$haystack" | "$haystack"/) true;;
() false;;
esac
)
fie.txtorfumis itself a symlink outsidefoo? – Stéphane Chazelas Jan 08 '14 at 12:12/fooor do you need to be able to pass arbitrary directories? I mean, is the question always with respect to./or not? – terdon Jan 08 '14 at 12:23