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).
Does "allowed storage of the target path within the data structures used for storing file information on disk (inodes)" mean that a fast symlink stores the path of the linked file inside the inode of the fast symlink
Yes
Does a fast symlink, as a file itself, actually only have an inode and has no file content?
Depends what you mean by "has file content". No symlinks have file content in the sense that you cannot open()
them and read()
from them. But in the meaning implied by the text you quoted, "The file contained the textual reference to the link’s target". So, yes, that textual reference can be considered the file's "content".
This content is the same regardless of whether the symlink is a fast symlink or a slow symlink. How and where the filesystem choses to store that information in its on-disk data structures is an implementation detail and does not affect this.
Does a slow symlink, as a file itself, have an inode and some file content which is the target path?
From that same point of view, yes!
What does "if the target path exceeds the available inode space" mean?
Depends on the filesystem and the kind of data structures it used to store inodes and how much spare space is in those data structures and whether they are variable-sized or fixed size. The maximum length of the target path of a symlink before it has to fall back to being stored as a slow symlink is an filesystem implementation detail.
By the way, nothing prevents a particular filesystem from using the same trick to store the contents of a short regular file to save space and disk access.
Is there any command that can check if a symlink is a fast or slow one?
At best, filesystem debugging or dumping tools. And it will be completely dependent on the type of filesystem you are interested in (xfs, ext*, btrfs, etc...)
When a symlink has file content, what is the command to show the content of the symlink? (So that if a fast symlink doesn't have file content and a slow one has, we can verfiy that.)
You can obtain the target path (contents) of a symlink with readlink
, but ls -l
will work too.
Best Answer
From the
man
page: