How can I search for symlinks that point to other symlinks? [closed]

Solution 1:

Following the entire link chain

realpath

realpath(1) gets you the absolute destination of a link. But it only gives you the final destination, so if you have a link chain, it will only show you the path of the last real file/folder that the chain eventually points to.

So in your case, if ../../e/f/g/h is real file or folder, you will see the absolute path. But if one of the components is also a symlink, it will keep recursively until it resolves all the symlinks in the way.

readlink

You could also use the readlink(1) command with the -f or -e flags, which will give you similar result to realpath.

  -f, --canonicalize            canonicalize by following every symlink in
                                every component of the given name recursively;
                                all but the last component must exist
  -e, --canonicalize-existing   canonicalize by following every symlink in
                                every component of the given name recursively,
                                all components must exist

Avoid following the entire link chain

If I understand correctly, you don't want to follow the chain to the last component, but to only get the absolute path of the actual link. It's a little ugly, but you can do something such as:

LINK=./a/b/c/d
/usr/bin/realpath --no-symlinks "$(dirname ${LINK})/$(readlink ${LINK})"

Explanation:

$ dirname $LINK # dirname ./a/b/c/d
./a/b/c

$ readlink $LINK # readlink ./a/b/c/d
../../e/f/g/h

$ echo "$(dirname ${LINK})/$(readlink ${LINK})" 
./a/b/c/../../e/f/g/h

$ /usr/bin/realpath --no-symlinks "$(dirname ${LINK})/$(readlink ${LINK})"
/home/a/e/f/g/h

If you only want to see the link relative to your CWD, you can add the --relative-to argument:

$ /usr/bin/realpath --no-symlinks --relative-to ./ "$(dirname ${LINK})/$(readlink ${LINK})"
a/e/f/g/h

Using find

You can get the argument for the realpath command I've shown before by using the find command:

$ find ./ -type l -printf "%h/%l\n"
./a/b/c/../../e/f/g/h

From man find:

%h     Leading directories of file's name (all but the last element).   If
       the  file  name  contains  no  slashes  (since it is in the current
       directory) the %h specifier expands to ".".

%l     Object of symbolic link (empty string if file is not a symbolic link).

Then you could combine it with the realpath command from above.

$ find ./ -type l -printf "%h/%l\n" | xargs /usr/bin/realpath -s
/home/a/e/f/g/h

If you only want to see the link relative to your CWD:

$ find ./ -type l -printf "%h/%l\n" | xargs /usr/bin/realpath -s --relative-to ./
a/e/f/g/h