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*'
Related
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.
I have a script that recursively searches all directories for specific files or specific file endings.
These certain files I want to save the path in a description file.
Looks for example like this:
./org/apache/commons/.../file1.pom
./org/apache/commons/.../file1.jar
./org/apache/commons/.../file1.zip
and so on.
In a blacklist , I describe which file endings I want to ignore.
! -path "./.cache/*" ! -path "./org/*" ! -name "*.sha1" ! -name"*.lastUpdated"
and so on.
Now i want to read this blacklist file while the search to ignore the described files:
find . -type f $(cat blacklist) > artifact.descriptor
Unfortunately, the blacklist will not be included while the search.
When:
echo "find . -type f $(cat blacklist) > artifact.descriptor"
Result is as expected:
find . -type f ! -path "./.cache/*" ! -path "./org/*" ! -name "*.sha1" ! -name"*.lastUpdated" > artifact.descriptor
But it does not work or exclude the described files.
I tried with following command and it works, but i want to know why not with with find alone.
find . -type f | grep -vf $blacklist > artifact.descriptor
Hopefully someone can explain it to me :)
Thanks a lot.
As tripleee suggests, it is generally considered bad practice to store a command in a variable because it does not catch all the cornercases.
However you can use eval as a workaround
/tmp/test$ ls
blacklist test.a test.b test.c
/tmp/test$ cat blacklist
-not -name *.c -not -name *.b
/tmp/test$ eval "find . -type f "`cat blacklist`
./test.a
./blacklist
In your case I think it fails because the quotes in your blacklist file are considered as a literal and not as enclosing the patterns and I think it works if you remove them, but still it's probably not safe for other reasons.
! -path ./.cache/* ! -path ./org/* ! -name *.sha1 ! -name *.lastUpdated
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.
$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
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.