[lug] "find" question

David Morris lists at morris-clan.net
Tue Aug 17 14:15:37 MDT 2004


On Tue, Aug 17, 2004 at 11:13:44AM -0600, Daniel Webb wrote:
> On Tue, 17 Aug 2004, David Morris wrote:
> 
> > I could be wrong, but it sounds like what you are trying to
> > do is find files in a symbolically linked directory tree
> > which are *not* symbolic links themselves.
> >
> > If that is correct, this is fairly easy to do combined with
> > grep and awk (all one line, but too long for email):
> >
> >     find . -follow -type -f \
> >         -exec ls -l {} \; | grep -v ^l | awk '{print $9}'
> >
> > Or, if you wanted to find *only* symbolic links to files,
> > just drop '-v' from the grep statement and leave everything
> > else the same.
> >
> > Note that while you can put the pipe to grep and awk within
> > the exec statement (which ends with '\;'), though with a bit
> > of difficulty, you don't really want to do that as it'd be
> > much slower.  `ls`, though, must be inside the -exec
> > statement to avoid a command-line that is too long when
> > searching large directory structures.
> 
> That makes sense, although I like my "-follow-dir" addition to find better
> :)

I agree, it would be convenient if you do such a search
commonly.

> What if the filenames have spaces in them?  I looked at the documentation
> I have for awk, but didn't see anything for multiple fields.  I thought
> of:
> 
> find . -follow -type -f \
>        -exec ls -l {} \; | grep -v ^l | sed 's/  / /g' \
>        cut --delimiter=' ' --fields=9-
> 
> but that will mess up files with multiple spaces in them.
> 
> Any suggestions for that case?

Spaces in filenames are evil and should be banned!
Floggings will begin at 10AM tomorrow. =)

You're on the right track using sed.  Try this:

    find . -follow -type f -exec ls -l {} \; | grep -v ^l \
        | sed 's/^.\{56\}//'

That regex in sed will remove the first 56 characters from
the line, which is everything before the filename begins on
a standard linux 'ls -l' listing.  There is a way to do the
same in awk, but I don't recall the syntax off the top of my
head.

Of course, if that final list is then input to another
program rather than being read by you, that is another
matter entirely.  If you are doing that, I suggest the
following, replacing 'foo --arg' with your target application and
arguments:

    find . -follow -type f -exec ls -l {} \; | grep -v ^l \
        | sed 's/^.\{56\}\(.*\)$/"\1"/' \
        | xargs -i foo --arg {}

Note that in sed, the pattern between '\(' and '\)' is
referenced by '\1' in the replacement string.

Or, if you *know* your resulting file list won't overflow
the maximum command length, you can pipe directly into
'foo --arg' rather than using xargs.

--David




More information about the LUG mailing list