Exclude range of directories in find command - linux

I have directory called test which has sub folders in the date range like 01,02,...31. This all sub folders contain .bz2 files in it. I need to search all the files with .bz2 extension using find command but excluding particular range of directories. I know about find . -name ".bz2" -not -path "./01/*", but writing -not -path "./01/*" would be so pathetic if I would want to skip 10 directories. So how would I skip 01..19 subdirectories in my find command ?

You can use wildcards in the pattern for the option -not -path:
find ./ -type f -name "*.bz2" -not -path "./0*/*" -not -path "./1*/*
this will exclude all directories starting with 0 or 1. Or even better:
find ./ -type f -name "*.bz2" -not -path "./[01]*/*"

Firstly, you can help find by using -prune rather than -not -path - that will avoid even looking inside the relevant directories.
To your main point - you can build a wildcard for your example (numeric 01 to 19):
find . -path './0[1-9]' -prune -o -path './1[0-9]' -prune -o -print
If your range is less convenient (e.g. 05 to 25) you might want to build the range into a bash variable, then interpolate that into the find command:
a=("-path ./"{05..25}" -prune -o")
find . ${a[*]} -print -prune
(you might want to echo "${a[*]}" or printf '%s\n' ${a[*]} to see how it's working)

For me, I found the find command as a standalone tool somehow cumbersome. Therefore, I always end up using a combination of find just for the recursive file search and grep to make the actual exculsion/inclusion stuff. Finally I hand over the results to a third command which will perform the actions, like rm to remove files for example.
My generic command would look something like this:
find [root-path] | grep (-v)? -E "cond1|cond2|...|condN" | [action-performing-tool]
root-path is where to start the search recursively
add -v option is used to invert the matching results.
cond1 - condN, the conditions for the matching. When -v is involed then this are the conditions to not match.
the action-performing-tool does the actual work
For example you want to remove all files not matching some conditions in the current directory:
find . -not -name "\." | grep -v -E "cond1|cond2|cond3|...|condN" | xargs rm -rf
As you can see, we are searching in the current directory indicated by the dot as root-path: then we want to invert the matching results, because we want all files not matching our conditions: and finally we pass all files found to rm in order to delete them: I add -rf to recursive/force delete all files. I used the find command with -not -name "." to exclude the current directory indicated normally by dot.
For the actuall question: Assume we have a directory using .git and .metadata directory and we want to exclude them in our search:
find . -not -name "\." | grep -v -E ".git|.metadata" | [action-performing-tool]
Hope that helps!

If you wan to exclude child directory under parent directory then this might be useful:
E.g.- You have parent directory "ParentDir" and it has two child directories "Child1, Child2". You wan to read files from "Chiled2" only and skip "Child1". Then this will help.
find ./ParentDir ! -path "./ParentDir/Child1*" -name *.<extention>

Related

Exclude hidden files and folders in linux find

I am trying to exclude hidden files and folders when doing a find in linux.
I have to exclude files or folders that start with a dot (.hidden) but also have to exclude folders that start with an # (like #eaDir).
So far I have the following command which seems to work but maybe there is a more elegant way?
find /path/to/start/search/ -not -path '*#eaDir*' -not -path "*/\.*" -type f -mtime -2
I did see examples using regular expression like so:
find . \( ! -regex '.*/\..*' \) -type f
but not sure how I would also exclude #eaDir directories with the -regexoption?
I believe there can also be hidden files that start with two dots? like "..hidden"? Is this already covered with my command or would I simply add a third option like -not -path "*/\..*" to exclude those as well?
Then I saw some examples of using -prune so that find won't descend in hidden directories, however I am unsure how I would use this correclty in my example. I would be interested in this to speed things up.
Thanks!
Use -not -name '.*'. This will exclude any names that begin with ..
Exclude files and folders starting with a . or an #:
find /path/to/start/search/ -not -path '*/[#.]*' -type f -mtime -2
Exclude files starting with a . and files and folders starting with a . or an #:
find /path/to/start/search/ -not -path '*/.*' -type f -mtime -2 | grep -v '/#.*/.*'

How can I find a file within a specific directory name?

So I need to find all files in /home/ with a file name of "options.php".
find . -name "options.php"
When 'in home', that will find all options.php files, however, I want to only find all options.php files when they are in /public_html/.
So in other words, it should ignore all other 'options.php' files found.
eg, positive/show results:
/home/usr1/public_html/options.php
/home/usr2/public_html/options.php
eg, shouldnt show me:
/home/usr1/public_html/wp-admin/options.php
/home/usr2/public_html/wp-content/plugins/whatever/options.php
You can pass a pattern via -path option as follows:
find /home/ -path '*/public_html/options.php'
For a more flexible pattern use -regex which accepts a regular expression applied on the whole path. But in this particular case -regex has no advantage over -path:
find /home/ -regex '.*/public_html/options.php'
Filter the desired results from the found results with grep.
find . -name "options.php" | grep 'public_html/options.php'
You can limit the depth of find:
find . -maxdepth N, this way It should only find options.php in your desired folder.
The ls utility is much better suited for this task:
ls -1 /home/*/public_html/options.php
If you want to process the result list and do not want to have an error message or warning in case no such files are found, then simply redirect the error output of the command:
ls -1 /home/*/public_html/options.php 2>/dev/null
An alternative using the find utility would be:
find /home -path "*/public_html/options.php"
Or, if you want to prevent matches in folders called "public_html" further down in the hierarchy:
find /home -path "/home/*/public_html/options.php"
find /home -maxdepth 3 -path "*/public_html/options.php"

Is there a way to specify exceptions to exclusions in the find command via Bash?

I would like to find all of the files within a directory and its subdirectories except for any settings files and anything in settings or dependency directories.
For example, I want to exclude from my results entire directories like .git, .idea, and node_modules as well as files like .DS_Store and config.codekit, but I want to include .gitignore.
What I want is something like the results of the following Git command, but including any untracked files and able to be easily and safely operated upon (e.g., to change permissions).
git ls-tree -r master --name-only
Here is what I have so far and although it is rather unwieldy it seems to mostly do what I want except for leaving out .gitignore:
find . -type f -not -name ".*" -not -name config.codekit -not -path "./.*" -not -path "./node_modules/*"
I have experimented with -prune without much success.
Is there a way to specify exceptions to exclusions in the find command via Bash—to say something like exclude all the things that match this pattern EXCEPT this thing or these things?
By the way, I am presently using OS X, but I also use Ubuntu and I plan to try Ubuntu on Windows when the Windows 10 Anniversary Update is generally available, so ideally I would like to have a command that works across all of those.
Thank you in advance for any solutions, insights, or optimizations!
Update
Thanks to help from gniourf-gniourf, I have revised my command. This seems to do what I wanted:
find . -type f \( \! -name ".*" \! -name config.codekit \! -path "./.*" \! -path "./node_modules/*" -o -name .gitignore \)
A quick example first: to find all files, pruning the .git directories and ignoring the .DS_Store files:
find . -name .git -type d \! -prune -o \! -name .DS_Store -type f
For example, I want to exclude from my results entire directories like .git, .idea, and node_modules as well as files like .DS_Store and config.codekit, but I want to include .gitignore.
find . \( -name .git -o -name .idea -o -name node_modules \) -type d \! -prune -o \! -name .DS_Store \! -name config.codekit -type f
When building your command, make sure you stick with the POSIX standard: it's a guarantee that your command will work on any (POSIX compliant) system. For example, -not is not POSIX compliant: use ! instead (you'll have to escape it so as to not clash with your shell history expansion).
Is there a way to specify exceptions to exclusions in the find command via Bash—to say something like exclude all the things that match this pattern EXCEPT this thing or these things?
Find files, excluding everything (pattern *) except the files one and two:
find . \( \! -name '*' -o -name one -o -name two \) -type f

Find all files contained into directory named

I would like to recursively find all files contained into a directory that has name “name1” or name “name2”
for instance:
structure/of/dir/name1/file1.a
structure/of/dir/name1/file2.b
structure/of/dir/name1/file3.c
structure/of/dir/name1/subfolder/file1s.a
structure/of/dir/name1/subfolder/file2s.b
structure/of/dir/name2/file1.a
structure/of/dir/name2/file2.b
structure/of/dir/name2/file3.c
structure/of/dir/name2/subfolder/file1s.a
structure/of/dir/name2/subfolder/file2s.b
structure/of/dir/name3/name1.a ←this should not show up in the result
structure/of/dir/name3/name2.a ←this should not show up in the result
so when I start my magic command the expected output should be this and only this:
structure/of/dir/name1/file1.a
structure/of/dir/name1/file2.b
structure/of/dir/name1/file3.c
structure/of/dir/name2/file1.a
structure/of/dir/name2/file2.b
structure/of/dir/name2/file3.c
I scripted something but it does not work because it search within the files and not only folder names:
for entry in $(find $SEARCH_DIR -type f | grep 'name1\|name2');
do
echo "FileName: $(basename $entry)"
done
If you can use the -regex option, avoiding subfolders with [^/]:
~$ find . -type f -regex ".*name1/[^/]*" -o -regex ".*name2/[^/]*"
./structure/of/dir/name2/file1.a
./structure/of/dir/name2/file3.c
./structure/of/dir/name2/subfolder
./structure/of/dir/name2/file2.b
./structure/of/dir/name1/file1.a
./structure/of/dir/name1/file3.c
./structure/of/dir/name1/file2.b
I'd use -path and -prune for this, since it's standard (unlike -regex which is GNU specific).
find . \( -path "*/name1/*" -o -path "*/name2/*" \) -prune -type f -print
But more importantly, never do for file in $(find...). Use finds -exec or a while read loop instead, depending on what you really need to with the matching files. See UsingFind and BashFAQ 20 for more on how to handle find safely.

How to remove all files NOT ending with certain formats?

So to remove all files ending with .lnx, the cmd would be rm *.lnx, right?
If I want to remove all files that do NOT end with [.lnx], what command should I use?
Is there such a thing?
You can use this:
$ rm !(*.lnx)
!(pattern-list)
Matches anything except one of the given patterns.
A pattern-list is a list of one or more patterns separated by a ‘|’.
find . -depth 1 -type f -not -name '*.lnx' -delete
find all files (-type f) in the current directory (-depth 1) which do not match the filename (-not -name '*.lnx'), and delete them (-delete)
As always, test this first. Run it without the -delete to see all the files that match.
ls | grep -v '\.lnx$' | xargs rm

Resources