LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Slackware (http://www.linuxquestions.org/questions/slackware-14/)
-   -   'find': filter out subdirectories (http://www.linuxquestions.org/questions/slackware-14/find-filter-out-subdirectories-4175442940/)

narke 12-25-2012 11:01 PM

'find': filter out subdirectories
 
Hi,

A question about 'find' command.

Suppose I have a tree, which contains subdirectories named from 'a' to 'z'. Under 'a', there is second level of subdirectories named from 'a1' to 'a100'.

Now I want a full list of files that reside in directories of a2 or [b-z], so I come up with the following command:

$ find . -path "./a/*" ! -path "./a2/*" -prune -o -type f -o -print.

But it doesn't work, everything under 'a' (including 'a2') disappeared from the output, that is not what I expected.

What's wrong with my command line?

Thanks in advance.

-woody

gnashley 12-26-2012 03:32 AM

'-maxdepth' is the option you want.

David the H. 12-26-2012 02:14 PM

Here are a couple of good links about using find:
http://mywiki.wooledge.org/UsingFind
http://www.grymoire.com/Unix/Find.html

The basic syntax of a find command is like this:
Code:

find <starting dirs> <global options> <matching expressions> <actions> [-a/-o <matching expressions> <actions>]
There can be multiple entries for all of these.

One thing to understand about -prune is that it's an action. That is, it removes from consideration all the files matched by the expressions in front of it. So to fully use it, you also need to include a second expression that does what you want with the rest of the files.

A second thing to watch out for is that the default action (print) only works if there's a single matching expression to operate on. When you have multiple sets of matching expressions, each one must have its actions explicitly provided, and generally grouped with \(..\) brackets (see the last section of my first link).

So what you really want is something more like this:
Code:

find . ! \( -path "*/a2/*" -prune \) -o \( -type f -print \)
Although actually I don't think even this is necessary for most of your needs. Since it all comes down to a properly matching -path expression anyway, just use that directly:

Code:

find . -type f -path "*/a2/*" -print
find . -type f -path "./[b-z]/* -print

Be aware that some options like -name and -path use globbing patterns, while others like -regex use regular expressions. Check the find man page.

narke 12-26-2012 09:49 PM

Hi, please see my commands inline:

Quote:

Originally Posted by David the H. (Post 4857260)
Here are a couple of good links about using find:
http://mywiki.wooledge.org/UsingFind
http://www.grymoire.com/Unix/Find.html

The basic syntax of a find command is like this:
Code:

find <starting dirs> <global options> <matching expressions> <actions> [-a/-o <matching expressions> <actions>]
There can be multiple entries for all of these.

One thing to understand about -prune is that it's an action. That is, it removes from consideration all the files matched by the expressions in front of it. So to fully use it, you also need to include a second expression that does what you want with the rest of the files.

A second thing to watch out for is that the default action (print) only works if there's a single matching expression to operate on. When you have multiple sets of matching expressions, each one must have its actions explicitly provided, and generally grouped with \(..\) brackets (see the last section of my first link).

So what you really want is something more like this:
Code:

find . ! \( -path "*/a2/*" -prune \) -o \( -type f -print \)
David, this is totally wrong. It will give me all the files which have "/a2/" in their name.
It is wrong because:
1, it won't list files such as b/x/y since there is not '/a2/' in the name;
2, it won't list files such as a/a2/dir/foo, since the '-prune' stops at 'a/a2/dir'.


Although actually I don't think even this is necessary for most of your needs. Since it all comes down to a properly matching -path expression anyway, just use that directly:

Code:

find . -type f -path "*/a2/*" -print
find . -type f -path "./[b-z]/* -print

I am looking for single command line solution because I need to redirect the output to a pipe.

Be aware that some options like -name and -path use globbing patterns, while others like -regex use regular expressions. Check the find man page.


narke 12-26-2012 09:52 PM

Quote:

Originally Posted by gnashley (Post 4856915)
'-maxdepth' is the option you want.

Hi, gnashly,

I don't see why '-maxdepth' has anything to do with my problem. Can you explain a little in further? Thanks.

Loomx 12-26-2012 10:20 PM

Can you just combine the last two lines that David the H gave you?

Code:

find . \( -path './a/a2/*' -o -path './[b-z]/*' \) -type f -print

David the H. 12-27-2012 04:07 PM

Again, try to take the time to understand how the programs work. As pointed out, you can combine test expressions with grouping brackets.


On a higher level, you can combine the output of multiple commands in your shell through several different types of command grouping.

Code:

# With command grouping brackets
{
        find . -type f -path "*/a2/*" -print
        find . -type f -path "./[b-z]/*" -print
} | output_command


# With a subshell
(
        find . -type f -path "*/a2/*" -print
        find . -type f -path "./[b-z]/*" -print
) | output_command


# With process substitution
output_command < <(
        find . -type f -path "*/a2/*" -print
        find . -type f -path "./[b-z]/*" -print
        )

# With a function
findfiles(){

        for pattern; do
                find . -type f -path "$pattern" -print
        done

} | output_command

findfiles '*/a2/*' './[b-z]/*' | output_command

Of course the output will be simply that of the separate commands, one after the other, unless you insert some way to sort it manually.


All times are GMT -5. The time now is 12:13 PM.