What you're asking for doesn't make much sense in the general case, so it's not surprising that find
has no provision for it.
A symlink with a relative target is relative to the path of the symlink. So for instance, if by traversing a directory by following symlinks, find
encounters a/b/c/d
and a
, a/b
, a/b/c
are all relative or absolute symlinks (or symlinks to paths with symlink components), what should it do?
If you're looking for a find
predicate or a GNU -printf
%
directive that expands to a symlink-free path to the file relative to the current directory or any directory, I'm afraid there's none.
If you're on Linux, you can get the absolute path of those files with:
find -L foo -type f -exec readlink -f {} \;
As you found out, there exists at least one realpath
command which accepts more than one path argument which in combination with the standard -exec cmd {} +
syntax is going to be a lot more efficient since it's running as few realpath commands as necessary:
find -L foo -type f -exec realpath {} +
find -L foo -type f -print0 | xargs -r0 realpath
might be quicker as if more than one realpath
command is needed, find
can keep on looking for more files while the first realpath
starts working which even on a single processor system might make it more efficient.
-print0
and xargs -r0
are not standard, come from GNU but are found in a number of other implementations like most modern BSDs.
Zsh has builtin support for it:
print -rl foo/***/*(-.:A)
If you don't care about the sorting order, you can disable sorting and make it a bit more efficient with:
print -rl foo/***/*(-.oN:A)
If you want to convert those to relative paths to the current directory, you could have a look at that SO question.
If you know that all those files have an absolute canonical path (whose none of the components are symlinks) inside the current directory, you can simplify it to (still with zsh
):
files=(foo/***/*(-.:A))
print -rl -- ${files#$PWD/}
Though short and convenient, and works whatever character filenames contain, I doubt it would faster than find
+ realpath
.
With the Debian realpath
and GNU tools, you can do:
cd -P .
find -L foo -type f -exec realpath -z {} + |
gawk -v p="$PWD" -v l="${#PWD}" -v RS='\0' -vORS='\0' '
substr($0, 1, l+1) == p "/" {$0 = substr($0, l+2)}; 1' |
xargs -r0 whatever you want to do with them
As I realise now, there's now a realpath
in recent versions of GNU coreutils, which has the exact feature you're looking for, so it's just a matter of
find -L foo -type f -print0 |
xargs -r0 realpath -z --relative-base . |
xargs -r0 whatever you want to do with them
(use --relative-to .
instead of --relative-base .
if you want relative paths even for files whose symlink free path doesn't reside below the current working directory).
Try find.
find -L /gluster_broken -mindepth 10
to find the link loops
then a non recursive rm
on the erroneous file(s)
find will follow links and find the same "too many levels" error. I use -mindepth to filter out anything less than 10 deep to avoid the ok files/directories. Yes, this does assume that you don't have more that 10 deep in your normal tree. All this command is trying to do is find the file in error.
-- edit
I think following command is better,
find -L /gluster_broken >/dev/null
Here's my test
$ find .
.
./dira
./dira/a
./dira/dirb
./dira/dirb/dirc
./dira/error
./dira/b
./dira/test
./dira/test/ab&<cd.file
./dira/test/magic?newlines
./dira/test/cleanup
$ find -L . >/dev/null
find: ‘./dira/a’: Too many levels of symbolic links
find: ‘./dira/error’: Too many levels of symbolic links
find: ‘./dira/b’: Too many levels of symbolic links
$
--- edit 2
I think my suggestion (comment) to check file-system might be best, I have just seen this answer and wonder if you have a similar issue.
Best Answer
You can identify cyclic symlinks with a bit of
find
trickery, try this:This works by filtering for symlinks, then excluding anything where the type of the symlink's target is any of the possible inode types. The only things are left are those where
find
can't determine the type of the target, which only happens for cyclic symlinks (broken ones match-xtype l
)