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.txt
orfum
is itself a symlink outsidefoo
? – Stéphane Chazelas Jan 08 '14 at 12:12/foo
or 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