find prune not working - shell

I am trying to find all ruby files in the project. However I want to ignore all the files residing under directory vendor.
find . -name .vendor -prune -o -name '*.rb' -print
Above command is not working. Anyone knows the fix?

Try:
find . -name '*.rb' ! -wholename "./vendor/*" -print
You may have to escape ! (i.e. write \!) character depending on your shell.

Related

Excluding multiple filetypes with find

I have a folder with 20k plus Images and most gui filemanagers (like dolphin) aren't able to manage this amount of data.
So I decided to use the bash instead. My problem is the following:
most of the files are *.IMG or *.LBL files
I am not interested in those files. I look for the others
with find . -type f -not -name "*.LBL" I am able to see all files instead of the *.LBL
with find . -type f -not -name "*.IMG" I am able to see all files instead of the *.IMG
both is not very helpful, since it still fills my terminal
either combining both seems not to work:
find . -type f -not -name "*.LBL" -o -not -name "*.IMG"
What is the correct way to see the files inside a folder excluding multiple filesuffixes?
Group conditions, I think -o -not isn't working as expected. Try this:
find . -type f -not \( -name "*.LBL" -o -name "*.IMG" \)
You can use bash's extended pattern matching (Might have to be turned on in a script with shopt -s extglob; usually enabled by default in an interactive shell):
printf "%s\n" !(*.LBL|*.IMG)

Bash script for removing specific file from certain subdirectories

On a unix server, I'm trying to figure out how to remove a file, say "example.xls", from any subdirectories that start with v0 ("v0*").
I have tried something like:
find . -name "v0*" -type d -exec find . -name "example.xls" -type f
-exec rm {} \;
But i get errors. I have a solution but it works too well, i.e. it will delete the file in any subdirectory, regardless of it's name:
find . -type f -name "example.xls" -exec rm -f {} \;
Any ideas?
You will probably have to do it in two steps -- i.e. first find the directories, and then the files -- you can use xargs to make it in a single line, like
find . -name "v0*" -type d | \
xargs -l -I[] \
find [] -name "example.xls" -type f -exec rm {} \;
what it does, is first generating a list of viable directory name, and let xargs call the second find with the names locating the file name within that directory
Try:
find -path '*/v0*/example.xls' -delete
This matches only files named example.xls which, somewhere in its path, has a parent directory name that starts with v0.
Note that since find offers -delete as an action, it is not necessary to invoke the external executable rm.
Example
Consider this directory structure:
$ find .
.
./a
./a/example.xls
./a/v0
./a/v0/b
./a/v0/b/example.xls
./a/v0/example.xls
We can identify files example.xls who have one of their parent directories named v0*:
$ find -path '*/v0*/example.xls'
./a/v0/b/example.xls
./a/v0/example.xls
To delete those files:
find -path '*/v0*/example.xls' -delete
Alternative: find only those files directly under directory v0*
find -regex '.*/v0[^/]*/example.xls'
Using the above directory structure, this approach returns one file:
$ find -regex '.*/v0[^/]*/example.xls'
./a/v0/example.xls
To delete such files:
find -regex '.*/v0[^/]*/example.xls' -delete
Compatibility
Although my tests were performed with GNU find, both -regex and -path are required by POSIX and also supported by OSX.

In Bash, how do you delete all files with same name, except the one located in a specific folder?

I have a specific file which is found in several directories. Usually I delete all of them by using the syntax:
find . -name "<Filename>" -delete
However, I want to retain one file from a specific folder, say FOLDER1.
How do I do this using find? (I want to use find because I use -print before -delete to check what files I am deleting. I am apprehensive on using rm since there is danger of deleting files I want to keep.)
Thanks in advance.
You can do it with
find . -name "filename" -and -not -path "./path/to/filename" -delete
You will want either to make sure that the path expression is a relative one, including the initial ./, so that it's matched by the expression, or else use wildcards. So if you know that it's in a folder named myfolder, but you don't know the full path to it, you can use
find . -name "filename" -and -not -path "*/myfolder/filename" -delete
If you don't want to delete anything under any directory named FOLDER1, you can tell find not to recurse down any directory so named at all, using -prune:
find . -name FOLDER1 -prune -o -name filename -delete
This is more efficient than recursing down that directory and then filtering out results that include it later.
Side note: When testing this, be sure you use the explicit -print:
find . -name FOLDER1 -prune -o -name filename -print
...whereas an implicit one won't behave as you expect:
# not what you want: equivalent to the below, not the above:
find . -name FOLDER1 -prune -o -name filename
...will behave as:
find . '(' -name FOLDER1 -prune -o -name filename ')' -print
...which thus includes contents on either side of the -o operator for the action.

Why is find finding .git directories?

I have the following find command and I'm surprised to see .git directories being found. Why?
$ find . ! -name '*git*' | grep git
./.git/hooks
./.git/hooks/commit-msg
./.git/hooks/applypatch-msg.sample
./.git/hooks/prepare-commit-msg.sample
./.git/hooks/pre-applypatch.sample
./.git/hooks/commit-msg.sample
./.git/hooks/post-update.sample
Because find searches for files and none of the found files have the search pattern in their name (see the man page). You need to remove the offending directory via the -prune switch:
find . -path ./.git -prune -o -not -name '*git*' -print |grep git
See Exclude directory from find . command
[edit] An alternative without -prune (and much more natural imho):
find . -not -path "*git*" -not -name '*git*' |grep git
You're just seeing expected behaviour of find. The -name test is only applied to the filename itself, not the whole path. If you want to search everything but the .git directory, you can use bash(1)'s extglob option:
$ shopt -s extglob
$ find !(.git)
It doesn't really find those git-files. Instead it finds files under ./.git/ that match the pattern ! -name '*git*' which includes all files that don't include git in their filename (not path name).
Finds -name is about the files, not the path.
Try -iwholename instead of -name:
find . ! -iwholename '*git*'
This is what I needed:
find . ! -path '*git*'

-prune in find working without OR(-o) option - Unix

$pwd
/tmp
$touch 1.tst 2.tst
$mkdir inner_dir
$touch inner_dir/3.tst
$find . ! -name . -prune -name '*.tst'
1.tst
2.tst
I want to restrict 'find' to search only to the current directory for the files with 'tst' extension (I know this can be done with 'ls' command, but want to add other 'find' filters like mtime later on).
My question is how the above 'find' works?.
Why doesn't the following work(with an OR option)?
find . ! -name . -prune -o -name '*.tst'
Thanks.
-prune
Always evaluates to the value True. Stops the descent of the current path name if it is a directory. If the -depth flag is specified, the -prune flag is
ignored.
I think if you play with it, you can figure out what it is doing.
e.g.
find . ! -name . -prune
gives
./1.tst
./2.tst
./d
We don't go down into ./d because of the prune -- "Stops the descent ...". What is left is then filtered by the -name '*.tst' to be just the list files at the top directory.
HTH

Resources