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 16, 2010 8:43 PM in response to GaryKing

If you do not want to install the GNU 'find' command, here is a script that, while even slower than the 'find' command should also locate all symlinks that point to a specific file:

#!/usr/bin/env bash
if [[ $# != 2 ]]; then
echo 1>&2 "Usage: ${0##*/} /starting_directory /path/to/reference_file"
exit 1
fi
START="$1"
export REFFILE="$2"
find $START -type l | perl -ne '
if ( ! $initialized ) {
$initialized = 1;
$reffile = $ENV{"REFFILE"};
@ref = stat($reffile);
}
chomp;
@st = stat($_);
if ( $st[0] == $ref[0] && $st[1] == $ref[1] ) {
print `ls -l "$_"`;
}
'

Sep 16, 2010 8:52 PM in response to GaryKing

Without having to install the GNU 'find' command, then from an admin-privileged login,

sudo -s
find / -type l -print0 | xargs -0 ls -TalkOs | sudo grep ' ->' | grep {filenameOfRealFile}

use just enough of the filename, to which any/all links point, to uniquely identify it. That is, I wouldn't include the filepath because, for example, links in the same directory won't display with the arrow followed by path.

As an example, on my computer I have a link in /etc/periodic/weekly that points to the real executable in /usr/local. When I type the above command to search for links, I get

iMac:jv imac$ sudo -s
Password:
iMac:jv root# find / -type l -print0 | xargs -0 ls -TalkOs | grep ' -> ' | grep diskPM
4 lrwxr-xr-x 1 root wheel - 17 Jul 12 21:42:30 2010 /private/etc/periodic/weekly/600.diskPM -> /usr/local/diskPM


the find -type l looks for links, the -print0 and xargs -0 handle white space in file names, and the two greps filter out everything but the real file name for which you are searching for its links.

Message was edited by: j.v.

....or, I guess you could run BobHarris' script.

Message was edited by: j.v.

Sep 16, 2010 9:02 PM in response to BobHarris

OK, I was having fun. So here is a different implementation that moves the 'find' inside of the perl script:

#!/usr/bin/env bash
if [[ $# != 2 ]]; then
echo 1>&2 "Usage: ${0##*/} /starting_directory /path/to/reference_file"
exit 1
fi
START="$1"
REFFILE="$2"
perl -e '
use File::Find;
@ref = stat($ARGV[1]);
find sub {
@st = stat($File::Find::name);
if ( $st[0] == $ref[0] && $st[1] == $ref[1] ) {
print `ls -l "$_"`;
}
}, $ARGV[0];
' "$START" "$REFFILE"

Sep 17, 2010 4:03 PM in response to BobHarris

If you want to optimize, then why 2 grep's?

Just using j.v.'s example

But j.v. still had more straight forward solution, and easier to understand then my install GNU 'find', or use perl (but I had fun writing the perl 🙂 ).


Well, the OP wanted to "find all symbolic links pointing to a file", so

sudo find / -type l -ls

is the most straight-forward solution 🙂

Sep 17, 2010 8:27 PM in response to Tony T1

well yeah, I guess I interpreted his problem as wanting to find all symlinks pointing to a specific file of interest, hence all the added gobbledygook, rather than just find every instance of a symbolic link resident on his hard drive (otherwise, his subject line might have only been "Find all symbolic links").

And you're right, I guess I could do away with one of the greps ( +| grep ' -> '+ )

Sep 18, 2010 1:33 PM in response to corbaguy

The approaches that use pipes will break on names containing whitespace, whereas find's -exec switch will handle them correctly.

Actually j.v.'s version which used -print0 and xargs -0 would not break. The -print0 passes the filenames nul terminated, and xargs -0 accepts nul terminated file names and passes them to the command being invoked as a single ARGV argument.

Also the ones that just do "find | grep" do not really care about spaces in paths.

That being said, your approach using 'test -ef' combines the simplicity of a command line solution and getting the accuracy of my perl solution. Very well done.

At the very least, I think the OP (GaryKing) should award you a "Helpful" star, and since I think yours is the shortest, straight forward and accurate, if I were the OP, I would give you the "Solved" award.

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

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

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 Account.