[lug] "find" question

Tkil tkil at scrye.com
Tue Aug 17 15:38:14 MDT 2004


>>>>> "Daniel" == Daniel Webb <lists at danielwebb.us> writes:

Daniel> I want to search a directory tree, dereferencing symbolic
Daniel> directory links, but not dereferencing symbolic file links.
Daniel> What is the easiest way to accomplish this?

I'm not sure what you're trying to do here, and the later conversation
hasn't enlightened me.

What do you mean by "dereference" here?

>From the point of "find", I take "dereference" to mean either
examining the target file (as opposed to the link itself), or it can
mean traversing into a directory.

If you mean the latter, then I don't see the problem -- if it's a
symlink to a file, there's nothing to descend into, so there's no
problem.  If it's a symlink to a directory, "-follow" is what you
want.

So I assume you mean the former, in which case GNU find has "-xtype".

If you mean something else, please give us an example of what you're
actually trying to accomplish.

With regards to spaces and newlines in filenames -- 'find' has
"-print0", which works well with 'xargs' "-0".  Perl also has a "-0"
flag (see "man perlrun") if you want to do manipulation on the
filenames.

If your predicates get any more complicated, however, and if you do
have to be paranoid about well-formed file names, then I would
probably abandon the idea of doing this in shell, and resort to a
scripting language.  In my case, I'd use Perl (probably with
File::Find, but the setting the symlink flags properly is almost as
difficult as writing a basic traverser).  I'm sure that Python has
equivalent libraries.

But I still don't know what what you're looking for.  You can control
whether or not symlinked directories are traversed with "-follow" (and
maybe also "-prune"); you can control what type of files are printed
with "-type" and "-xtype".

Making a quick hierarchy with these commands:

| $ mkdir a b
| $ ln -s b c
| $ touch a/foo b/bar c/baz
| $ ( cd a ; ln -s ../b/bar ./lbar )

Looks like this:

| $ ls -lR
| .:
| total 0
| drwxr-xr-x  2 tkil users 16 Aug 17 15:23 a
| drwxr-xr-x  2 tkil users 16 Aug 17 15:23 b
| lrwxrwxrwx  1 tkil users  1 Aug 17 15:23 c -> b
| 
| ./a:
| total 0
| -rw-r--r--  1 tkil users 0 Aug 17 15:23 foo
| lrwxrwxrwx  1 tkil users 8 Aug 17 15:24 lbar -> ../b/bar
| 
| ./b:
| total 0
| -rw-r--r--  1 tkil users 0 Aug 17 15:23 bar
| -rw-r--r--  1 tkil users 0 Aug 17 15:23 baz

You can use 'find' like so:

| $ find . -type l
| ./a/lbar
| ./c
| $ find . -xtype l
| $ find . -follow -type l
| $ find . -follow -xtype l
| ./a/lbar
| ./c

If you want the names of directories (only) that are symbolic links:

| $ find . -follow -xtype l -type d
| ./c

And if you want to see what they point to:

| $ find . -follow -xtype l -type d -print0 | xargs -0 ls -al
| lrwxrwxrwx  1 tkil users 1 Aug 17 15:23 ./c -> b

But as I've said before (sorry for repeating myself!) I'm not sure
this is what you're looking for.

As for actually parsing out the name and target of a link, you're in
trouble: remember that the sequence " -> " is perfectly valid (if a
very bad idea) in unix file names, and you can't rely on the column
offset being the same either.  Ah-ha, there's "readlink" command:

| $ find . -follow -xtype l -type d -exec readlink '{}' \;
| b

If you need both, and are still paranoid about weird file names, I'd
again likely rely on a scripting language where I'm more comfortable
with the quoting situation:

| $ find . -follow -xtype l -type d -print0 | \
| >   perl -0 -lnwe 'my $d = readlink $_; print "$_ => $d\n";'
| ./c => b

Good luck,
t.



More information about the LUG mailing list