Using find for complex AND/OR string search - command-line

I use a script using find to search my multimedia collection. This previously worked until I began to add movie trailers, all of which use the naming format -trailer.
I want to use find to locate all files matching *.mkv, *.mp4, or *.avi, while excluding any file containing the string fragment "-trailer.", even if it matches the first criteria (ie, exclude file moviename-trailer.mkv). However, the following includes all trailers in the result.
find . -type f -iname '*.mkv' -or -iname '*.mp4' -or -iname '*.avi' -not -iname '*trailer.*'
I have changed the order of search without success, by trying to first exclude all trailers, and then search on the remainder.
I suspect that find encounters a file such as *-trailer.mkv, flags this as true and prints it, and then looks for the next file, before getting to the condition of excluding it.
Is there a way to nest the search with parenthesis following this logic: find all files (*.mkv OR *.mp4 OR *.avi) and not (*-trailer.*)?
Thanks much.

duda#coolomet:~/test$ touch a.mp4
duda#coolomet:~/test$ touch a-trailer.mp4
duda#coolomet:~/test$ find . -type f -iname '*.mkv' -or -iname '*.mp4' -or -iname '*.avi' -not -iname 'trailer.'
./a.mp4
./a-trailer.mp4
duda#coolomet:~/test$ find . -type f \( -iname '*.mkv' -or -iname '*.mp4' -or -iname '*.avi' \) -and -not -iname '*trailer.*'
./a.mp4
duda#coolomet:~/test$
You need to do a proper logical expression and have proper wildcards in iname arguments.

find . | grep -P '^(((?!-trailer).)*\.((mkv|avi|mp4)))$'
will return
./movie.avi
./movie.mkv
./movie.mp4
when the current directory contains the files
./movie-trailer.avi
./movie-trailer.mkv
./movie-trailer.mp4
./movie.avi
./movie.mkv
./movie.mp4

Related

find -print0 -name "* *" (whitespace)

Is there a way to pass all filenames containing whitespace to the pipe, or at least those containing spaces? To that end I attempted:
~/Desktop> find . -type f -mindepth 1 -maxdepth 1 -name "* *" | wc -c
0
~/Desktop> find . -type f -mindepth 1 -maxdepth 1 -print0 -name "* *" | wc -c
247
~/Desktop>
As you see, the command without -print0 accurately finds no files containing whitespace in the current directory. But with -print0 added, the -name option apparently is not interpreted as I would expect.
Even better than
-name "* *"
would be some way to specify any whitespace, not just space. Ideally this could be done in find, rather than resorting to "downstream" processing using a perl regular expression.
Ultimately, I want to pipe the filenames to a script that will replace whitespace by some character I specify.
Order matters. This will print every single name because the -print action comes before the -name filter:
find . -print -name "foo"
This will print only those named foo, because the action comes after the filter:
find . -name "foo" -print

cygwin bash find with prune option not behaving as expected

The two commands below are yielding exactly the same file list in bash under cygwin:
find ../../../../.. -name "*.o" -and -path "*/common/*"
find ../../../../.. -name "*.o" -and -path "*/common/*" -prune
This list includes files such as:
../../../../../platform/abc/common/ppng.o
../../../../../platform/abc/common/variant/pxx.o
The list does not include any files without "common" in their pathnames.
What I'm trying to do is find (and ultimately eliminate) object files in all directories but any that have a "common" directory component. I've tried about 25 other combinations without luck.
Any pointers?
afaik, -path doesn't take regular expressions. I think what you want to do is find all your object files (.*.o) and exclude all the common directories (.*/common/.*):
find . -regex '.*.o' -and -not -regex '.*/common/.*'
You can make it all case insensitive with -iregex if needed.

clearcase: find -name not allow multiple patterns?

I wanna find *.cs and *.cpp files through cleartool find command. But it failed.
cleartool find "M:\test_view\code" -name "*.cs *.cpp" -print
Nothing can be found based on above even there are matched files in that folder.
How to set multiple file-name patterns ?
The query language offer some possibility for Compound queries (query || query)
But the cleartool find has none of those operators for the -name option.
The best you can do, following the cleartool wildcard syntax, is
cleartool find "M:\test_view\code" -name "*.c[sp]*" -print
This is a bit late but perhaps this will help someone.
One option is to wrap this is a for loop:
:: namelist.txt contains a list of file types ( *.cs, *.cpp, )
FOR /F "tokens=1" %%A IN (c:\bin\namelist.txt) DO ( cleartool find "M:\test_view\code" -all -type f -name %%A -print)
It lookslike cleartool wraps the unix style find utility.
If that is right you might be able to use '-or'
$ find -type f -name '*.cs' -or '*.cpp' -print

How do I prevent find from printing .git folders?

I have a find command that I run to find files whose names contain foo.
I want to skip the .git directory. The command below works except it prints an
annoying .git any time it skips a .git directory:
find . ( -name .git ) -prune -o -name '*foo*'
How can I prevent the skipped .git directories from
printing to the standard output?
So just for better visibility:
find -name '.git*' -prune -o -name '*foo*' -print
This also omits .gitignore files; note the trailing -print to omit printing, -prune stops descending into it but without -print prints it nevertheless. Twisted C;
find . -not -wholename "./.git*" -name "*foo*"
or, more strictly, if you don't want to see .git/ but do want to search in other dirs whose name also begins with .git (.git-foo/bar/...)
find . -not -wholename "./.git" -not -wholename "./.git/*" -name "*foo*"
If your .git/ directories may not always necessarily be located at the top-level of your search directory, you will want to use -not -wholename ".*/.git" and -not -wholename ".*/.git/*".
A bit odder but more efficient because it prunes the whole .git dir:
find . -not \( -wholename "./.git" -prune \) -name "*foo*"
Try this one:
find . -name '*foo*' | grep -v '\.git'
This will still traverse into the .git directories, but won't display them. Or you can combine with your version:
find . ( -name .git ) -prune -o -name '*foo*' | grep -v '\.git'
You can also do it without grep:
find . ( -name .git ) -prune -printf '' -o -name '*foo*' -print

Linux find command gotcha?

Hi I am trying to find all js & css files in one find command. I tried all of the below but in vain:
find WebContent -name "*.[jc]ss?"
find WebContent -name "*.[jc]ss{0,1}"
find WebContent -name "*.[jc][s]{1,2}$"
find WebContent -name "*.[jc]s{1,2}"
find WebContent -name "*.[jc]s[s]?"
Now what??
-name accepts arguments that are globs, not regular expressions. You could use -regex if you wanted to use regular expressions, but the -o option (meaning "or") is probably the simplest solution:
find WebContent -name "*.js" -o -name "*.css"
Try this
find WebContent -regextype posix-extended -regex '.*\.(css|js)$'
use the -iname option case insensitive
find WebContent \( -iname "*.js" -o -iname "*.css" \)
you can do boolean expressions with find. -o stands for OR.
find -name "*.js" -o -name "*.cpp"