Apple Event: May 7th at 7 am PT

Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Find all symbolic links pointing to a file

How do I find all symbolic links pointing to a file?

iMac, Mac OS X (10.6.4), 3.2 GHz Intel Core i3, 4 GB RAM

Posted on Sep 16, 2010 5:15 PM

Reply
22 replies

Sep 18, 2010 4:18 PM in response to Tony T1

{quote:title=Tony T1 wrote:}

Actually, running an exec and test for each link does not seem efficient at all.{quote}
That will depend on cardinality - if there are a smallish number of links in the FS (which is usually the case), then the filter should be pretty efficient (I have found such to be the case often, as the secondary filter remains cached in the OS filesystem buffers). If there are a lot of symlinks, then a pipeline may be more efficient, though in that case I'd be more inclined to use sed than one of the grep variants, since with grep you'd still need another filter to reduce the output to just a list of desired files.

Assuming that symlinks make up less than 1% of FS entries (usually a pretty safe assumption), and only a handful of elements match the test, I'd forego any pipelining and use it the way I wrote it. You might be surprised at how fast find's -exec switch really is.

Sep 18, 2010 5:14 PM in response to Tony T1

Actually, running an exec and test for each link does not seem efficient at all.

I never said efficient. But cobraguy's is more accurate, as it does not have any false positives.

And how many symlinks are there on the system when compared against work of walking the entire system directory tree and doing a stat() against every file on the system. I suspect that doing a 'test' on each symlink is noise.

For example, I did a test using your find|fgrep example below, and cobraguy's find -exec test. To save myself some time, I only tested against my home directory (684,596 files in my home directory tree and 2060 symlinks):

time find $HOME -type l -exec test {} -ef /path/to/filename ; -print
real 1m18.780s
user 0m2.802s
sys 0m13.433s
time find $HOME -type l -ls | fgrep filename
real 1m12.950s
user 0m1.906s
sys 0m9.517s
time perl -e '...' # actually script posted above
real 2m16.701s
user 0m13.912s
sys 0m56.181s

The delta was 6 seconds over 684,596 files. According to SuperDuper, I have 1,525,588 files on my system, so scaling up the above numbers says on my system the find|fgrep would have taken about 2m42.532 vs find -exec test would have taken about 2m55.521, or a delta of about 13 seconds.

And as a point of comparision I include my perl script solution which was twice as slow as either of the 'find' solutions, and used about 6 to 7 times as much CPU.

find / -type l -ls | fgrep "filename"

is not only more efficient, but will list partial filenames (why test for "Very Long Path To/A Very Long File Created By Tony.txt" when an fgrep for "Tony" will do

Nothing wrong with your approach, and as shown above it would be a bit more efficient, and most likely the few false positives are noise (unless the name was not very unique).

However, since the OP (GaryKing) has not checked in since his first post, we do not know any more details on his needs. Whether good enough is all he wants, or if accuracy is important.

Sep 18, 2010 5:56 PM in response to corbaguy

As find already knows which file the symbolic link points to once found, running another shell with 'test' can only be inefficient. The pipe to grep will not take as much time.

Here's the results with time. I ran the grep filter 1st, so any caching would only benefit the results of -exec test:


MacBook:~ Tony$ sudo time find / -type l -ls | fgrep "test.txt"
Password:
find: /dev/fd/3: Not a directory
find: /dev/fd/4: Not a directory
10473:6413779 8 lrwxr-xr-x 1 Tony staff 8 Sep 18 20:29 /Users/Tony/test2.txt -> test.txt
238.97 real 3.81 user 51.58 sys
MacBook:~ Tony$ sudo time find / -type l -exec test {} -ef "test.txt" ; -print
Password:
find: /dev/fd/3: Not a directory
find: /dev/fd/4: Not a directory
/Users/Tony/test2.txt
243.92 real 8.71 user 71.88 sys
MacBook:~ Tony$


edit: looks like Bob and I were testing at the same time, otherwise I would not have posted this.

Message was edited by: Tony T1

Sep 18, 2010 5:54 PM in response to BobHarris

BobHarris wrote:

Nothing wrong with your approach, and as shown above it would be a bit more efficient, and most likely the few false positives are noise (unless the name was not very unique).

However, since the OP (GaryKing) has not checked in since his first post, we do not know any more details on his needs. Whether good enough is all he wants, or if accuracy is important.


Why would there be any false positives?
Are you referring to my 'shortcut' of a partial file name?
If so, only need to fgrep the full path and filename and only that file will be filtered.

Sep 18, 2010 6:04 PM in response to Tony T1


sudo time find / -type l -ls | fgrep "test.txt"
...
238.97 real 3.81 user 51.58 sys
sudo time find / -type l -exec test {} -ef "test.txt" ; -print
...
243.92 real 8.71 user 71.88 sys

5 seconds difference (with the -exec getting the benefit of any caching).
That comes out to about 2%. I say noise.

In my opinion, a 2% performance sacrifice for accuracy, is a small price to pay.
And even if the cache is not warmed up, I do not think it would take all that much longer
as my testing shows.

And again, all this discussion is academic, as we do not know what the OP (GaryKing) really needs.
But it has been a fun discussion, and different tricks and approaches have been presented.
...running another shell with 'test' can only be inefficient

Actually 'find' would NOT run a shell, it would directly fork and exec the test command.
And test itself is a small program (46K).

Sep 18, 2010 6:11 PM in response to Tony T1

Why would there be any false positives?
Are you referring to my 'shortcut' of a partial file name?
If so, only need to fgrep the full path and filename and only that file will be filtered.

Symbolic links do NOT need to contain full paths. The can contain relative paths.

For example:

ln -s ../../bin/fred.real my.symlink.name
ln -s /Users/harris/bin/fred.read my.other.symlink

Both could be working symlinks, but only "/bin/fred.real" is unique, which could exist somewhere else.

Also a symlink can point to another symlink and still get to the real file, you would not see that with a find -ls.

Less likely, but the real file might have an alternate hardlink name with some symlinks pointer to the alternate hardlink name, but it is really the same file

ln /Users/harris/bin/fred.read fred.hardlink
ln -s fred.hardlink my.alternate.symlink

The 90% case would most likely be unique. But the more unique infomation put into the grep, the greater chance a symlink could be missed. The less unique information the more likely a false positive.

Find all symbolic links pointing to a file

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.