find, excluding dir, not descending into dir, AND using maxdepth and mindepth - unix

This is RHEL 5.6 and GNU find 4.2.27.
I am trying to exclude a directory from find, and want to make sure that directory isn't descended into. I've seen plenty of posts saying -prune will do this - and it does. I can run this command:
find . -type d -name "./.snapshot*" -prune -o -print
and it works. I run it through strace and verify it is NOT descending into .snapshot.
I also want to find directories ONLY at a certain level. I can use mindepth and maxdepth to do this:
find . -maxdepth 8 -mindepth 8 -type d
and it gives me all the dirs 8 levels down, including what's in .snapshot.
If I combine the prune and mindepth and maxdepth options:
find . -maxdepth 8 -mindepth 8 -type d \( -path "./.snapshot/*" -prune -o -print \)
the output is right - I see all the dirs 8 levels down except for what's in .snapshot, but if I run that find through strace, I see that .snapshot is still being descended into - to levels 1 through 8.
I've tried a variety of different combinations, moving the precedence parens around, reording expression components - everything that yields the right output still descends into .snapshot.
I see in the man page that -prune doesn't work with -depth, but doesn't say anything about mindepth and maxdepth.
Can anyone offer any advice?
Thanks...
Bill

I think this is your answer. -mindepth seems to turn off all test till that level. kind of a bummer.
(right from "man find")
-mindepth levels
**Do not apply any** tests or actions at levels less than levels (a non-negative integer).
'-mindepth 1' means process all files except the command line arguments.

I'm adding this as another answer because it's a different take on the issue. You could try the find command like this :
find * .[^s][^n]* (other switches/args)
It would avoid the .snapshot directory by using the shell instead of the find switches. granted it's not perfect but maybe it's close enough for what you are doing.

Related

List only folders which matches the wildcard expression

Is there a command which lists all folders that matches a wildcard expression? Example, if there are thousands of directories and I only want those ending in M or those starting in JO to be listed, can I do that with a certain Linux command? Thanks!
Use find command, for example:
# find anything that start with 'jo' end with 'm' (case insensitive)
find . -iname 'jo*m'
You can execute any command after that, for example:
# find just like above but case sensitive, and move them to `/tmp`
find . -name 'JO*M' -exec mv -v {} /tmp \;
To find only a directory, you can use -type d flag, for example:
# find any directory that start with JO
find . -name 'JO*' -type d
Explanation, first argument is the starting directory, . means current directory. The next argument means the search criteria -name for case sensitive search, -iname for case insensitive search, -type for type of item search, -exec to execute certain command where the {} is the file name matched. You can learn more here or for your specific case here.

Rename all subdirectories in csh shell

I'm using csh and I have a directory structure containing multiple sub-directories. I'm trying to rename all the directories and sub-directories but not the files inside these directories. So something like
From
topdir1
--dir11
--dir12
topdir2
--dir21
----dir211
--dir22
to
topdir1.test
--dir11.test
--dir12.test
topdir2.test
--dir21.test
----dir211.test
--dir22.test
I can list the directories with find . -maxdepth 3 -type d. I'm trying to use a foreach loop to rename them. So
foreach i (`find . -maxdepth 3 -type d`)
mv $i $i.test
end
But this doesn't work as once the top level directory is renamed, it cannot find the sub-directories, so it only renames the top level directories.
Any idea on how to go about this?
Thanks
How about reversing the find results so that the subdirectories are listed first?
foreach i (`find ./* -maxdepth 3 -type d | sort -r`)
mv $i $i.test
end
Sort will output the longest directory names last, using the -r (reverse) flag changes it so that the lowest directories will be listed first, and be renamed before their parent directories do.
Use the -depth option to find.
From the solaris man find page:
-depth Always true. Causes descent of the
directory hierarchy to be done so that
all entries in a directory are acted on
before the directory itself. This can
be useful when find is used with cpio(1)
to transfer files that are contained in
directories without write permission.
Why use a loop? Just let find do the work:
find . -depth -maxdepth 3 -type d -exec mv {} {}.test \;
That is not strictly portable (some implementations of find may legally not expand {}.test to the string you want, so you might prefer:
find . -depth -maxdepth 3 -type d -exec sh -c 'mv $0 $0.test' {} \;

Using mtime other than with FIND

I am trying to write a script which will move files older than 1 day to an archive directory. I used the following find command:
for filename in `find /file_path/*.* -type f -mtime +1`
This fails since my argument list is too big to be handled by find. I got the following error:
/usr/bin/find: arg list too long
Is it possible to use find in an IF-ELSE statement? Can someone provide some examples of using mtime other then in find.
Edit: To add the for loop of which the find is a part.
find /file_path -name '*.*' -mtime +1 -type f |
while read filename
do ...move operation...
done
That assumes your original code was acceptable in the way it handled spaces etc in file names,
and that there is no sensible way to do the move in the action of find. It also avoids problems with overlong argument lists.
Why not just use the -exec part of find?
If you just want to cp files, you could use
find /file_path -name "." -mtime +1 -type f | xargs -i mv {} /usr/local/archived

GNU find: Search in current directory first

how can I tell find to look in the current folder first and then continue search in subfolders? I have the following:
$ find . -iname '.note'
folder/1/.note
folder/2/.note
folder/3/.note
folder/.note
What I want is this:
$ find . -iname '.note'
folder/.note
folder/1/.note
folder/2/.note
folder/3/.note
Any ideas?
find's algorithm is as follows:
For each path given on the command line, let the current entry be that path, then:
Match the current entry against the expression.
If the current entry is a directory, then perform steps 1 and 2 for every entry in that directory, in an unspecified order.
With the -depth primary, steps 1 and 2 are executed in the opposite order.
What you're asking find to do is to consider files before directories in step 2. But find has no option to do that.
In your example, all names of matching files come before names of subdirectories in the same directory, so find . -iname '.note' | sort would work. But that obviously doesn't generalize well.
One way to process non-directories before directories is to run a find command to iterate over directories, and a separate command (possibly find again) to print matching files in that directory.
find -type d -exec print-matching-files-in {} \;
If you want to use a find expression to match files, here's a general structure for the second find command to iterate only over non-directories in the specified directory, non-recursively (GNU find required):
find -type d -exec find {} -maxdepth 1 \! -type d … \;
For example:
find -type d -exec find {} -maxdepth 1 \! -type d -iname '.note' \;
In zsh, you can write
print -l **/(#i).note(Od)
**/ recurses into subdirectories; (#i) (a globbing flag) interprets what follows as a case-insensitive pattern, and (Od) (a glob qualifier) orders the outcome of recursive traversals so that files in a directory are considered before subdirectories. With (Odon), the output is sorted lexicographically within the constraint laid out by Od (i.e. the primary sort criterion comes first).
Workaround would be find . -iname '.note' | sort -r:
$ find . -iname '.note' | sort -r
folder/.note
folder/3/.note
folder/2/.note
folder/1/.note
But here, the output is just sorted in reverse order and that does not change find's behaviour.
For me with GNU find on Linux I get both orderings with different test runs.
Testcase:
rm -rf /tmp/depthtest ; mkdir -p /tmp/depthtest ; cd /tmp/depthtest ; for dir in 1 2 3 . ; do mkdir -p $dir ; touch $dir/.note ; done ; find . -iname '.note'
With this test I get the poster's first result. Note the ordering of 1 2 3 .. If I alter this ordering to to . 1 2 3
rm -rf /tmp/depthtest ; mkdir -p /tmp/depthtest ; cd /tmp/depthtest ; for dir in . 1 2 3 ; do mkdir -p $dir ; touch $dir/.note ; done ; find . -iname '.note'
I get the poster's second result.
In either case adding -depth to find does nothing.
EDIT:
I wrote a perl oneliner to look in to this further:
perl -e 'opendir(DH,".") ; print join("\n", readdir(DH)),"\n" ; closedir(DH)'
And I ran this against /tmp/depthtest after running testcase 1 with these results:
.
..
1
2
3
.note
I ran it again after testcase 2 with these results:
.
..
.note
1
2
3
Which confirms that the results are in directory order.
The -depth option to find only controls whether e.g. ./1/.note is processed before or after ./1/, not whether ./.note or ./1/ is first, so the order of the results is purely based on directory order (which is mostly creation order).
It might be helpful to look at How do I recursively list all directories at a location, breadth-first? to learn how to work around this problem.
find -s . -iname ".note" doesn't help? or find . -iname '.note'|sort ?
Find in the current folder
find ./in_save/ -type f -maxdepth 1| more
==>73!

find without recursion

Is it possible to use the find command in some way that it will not recurse into the sub-directories? For example,
DirsRoot
|-->SubDir1
| |-OtherFile1
|-->SubDir2
| |-OtherFile2
|-File1
|-File2
And the result of something like find DirsRoot --do-not-recurse -type f will be only File1, File2?
I think you'll get what you want with the -maxdepth 1 option, based on your current command structure. If not, you can try looking at the man page for find.
Relevant entry (for convenience's sake):
-maxdepth levels
Descend at most levels (a non-negative integer) levels of direc-
tories below the command line arguments. `-maxdepth 0' means
only apply the tests and actions to the command line arguments.
Your options basically are:
# Do NOT show hidden files (beginning with ".", i.e., .*):
find DirsRoot/* -maxdepth 0 -type f
Or:
# DO show hidden files:
find DirsRoot/ -maxdepth 1 -type f
I believe you are looking for -maxdepth 1.
If you look for POSIX compliant solution:
cd DirsRoot && find . -type f -print -o -name . -o -prune
-maxdepth is not POSIX compliant option.
Yes it is possible by using -maxdepth option in find command
find /DirsRoot/* -maxdepth 1 -type f
From the manual
man find
-maxdepth levels
Descend at most levels (a non-negative integer) levels of directories below the starting-points.
-maxdepth 0
means only apply the tests and actions to the starting-points themselves.

Resources