Need to find files with multiples arguments in bash - bash

I've done this atm, I need to find in the main directory and in the sub-directory everything starting with the letter 'a', every files ending with 'z' and every files starting with 'z' and ending with 'a!'.
find . -name "a*" | find . "*z" -type f | find . "z*a!" -type f
I tried to be as clear as possible, sorry if it wasn't clear enough.

find . -type f \( -name 'a*' -or -name '*z' -or -name 'z*a!' \)
Use -o instead of -or for POSIX compliance.
If you really want to also find links, directories, pipes etc. starting with a but only files matching the remaining conditions, you can do
find . -name 'a*' -or -type f \(-name '*z' -or -name 'z*a!' \)

TL;DR
find . -name 'a*' -o -type f \( -name '*z' -o -name 'z*a!' \)
Explanations:
The find logical operators are -a (AND) and -o (OR). You use them to combine elementary tests. Note that because of operator's precedence you sometimes need parentheses and that they must be escaped (with \) to prevent their interpretation by the shell. Your test is:
everything starting with the letter 'a': -name 'a*'.
every files ending with 'z': -type f -a -name '*z'.
every files starting with 'z' and ending with 'a!': -type f -a -name 'z*a!'.
So the complete test could be:
-name 'a*' -o \( -type f -a -name '*z' \) -o \( -type f -a -name 'z*a!' \)
As -a is the default we can omit it, and as -type f (file) is common to the two last terms of the disjunction we can factor it:
-name 'a*' -o -type f \( -name '*z' -o -name 'z*a!' \)

Related

find and delete folder and/or zip file in a directory [duplicate]

I was trying to get a list of all python and html files in a directory with the command find Documents -name "*.{py,html}".
Then along came the man page:
Braces within the pattern (‘{}’) are not considered to be special (that is, find . -name 'foo{1,2}' matches a file named foo{1,2}, not the files foo1 and foo2.
As this is part of a pipe-chain, I'd like to be able to specify which extensions it matches at runtime (no hardcoding). If find just can't do it, a perl one-liner (or similar) would be fine.
Edit: The answer I eventually came up with include all sorts of crap, and is a bit long as well, so I posted it as an answer to the original itch I was trying to scratch. Feel free to hack that up if you have better solutions.
Use -o, which means "or":
find Documents \( -name "*.py" -o -name "*.html" \)
You'd need to build that command line programmatically, which isn't that easy.
Are you using bash (or Cygwin on Windows)? If you are, you should be able to do this:
ls **/*.py **/*.html
which might be easier to build programmatically.
Some editions of find, mostly on linux systems, possibly on others aswell support -regex and -regextype options, which finds files with names matching the regex.
for example
find . -regextype posix-egrep -regex ".*\.(py|html)$"
should do the trick in the above example.
However this is not a standard POSIX find function and is implementation dependent.
You could programmatically add more -name clauses, separated by -or:
find Documents \( -name "*.py" -or -name "*.html" \)
Or, go for a simple loop instead:
for F in Documents/*.{py,html}; do ...something with each '$F'... ; done
This will find all .c or .cpp files on linux
$ find . -name "*.c" -o -name "*.cpp"
You don't need the escaped parenthesis unless you are doing some additional mods. Here from the man page they are saying if the pattern matches, print it. Perhaps they are trying to control printing. In this case the -print acts as a conditional and becomes an "AND'd" conditional. It will prevent any .c files from being printed.
$ find . -name "*.c" -o -name "*.cpp" -print
But if you do like the original answer you can control the printing. This will find all .c files as well.
$ find . \( -name "*.c" -o -name "*.cpp" \) -print
One last example for all c/c++ source files
$ find . \( -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) -print
I had a similar need. This worked for me:
find ../../ \( -iname 'tmp' -o -iname 'vendor' \) -prune -o \( -iname '*.*rb' -o -iname '*.rjs' \) -print
My default has been:
find -type f | egrep -i "*.java|*.css|*.cs|*.sql"
Like the less process intencive find execution by Brendan Long and Stephan202 et al.:
find Documents \( -name "*.py" -or -name "*.html" \)
Braces within the pattern \(\) is required for name pattern with or
find Documents -type f \( -name "*.py" -or -name "*.html" \)
While for the name pattern with and operator it is not required
find Documents -type f ! -name "*.py" -and ! -name "*.html"
#! /bin/bash
filetypes="*.py *.xml"
for type in $filetypes
do
find Documents -name "$type"
done
simple but works :)
I needed to remove all files in child dirs except for some files. The following worked for me (three patterns specified):
find . -depth -type f -not -name *.itp -and -not -name *ane.gro -and -not -name *.top -exec rm '{}' +
This works on AIX korn shell.
find *.cbl *.dms -prune -type f -mtime -1
This is looking for *.cbl or *.dms which are 1 day old, in current directory only, skipping the sub-directories.
find MyDir -iname "*.[j][p][g]"
+
find MyDir -iname "*.[b][m][p]"
=
find MyDir -iname "*.[jb][pm][gp]"
What about
ls {*.py,*.html}
It lists out all the files ending with .py or .html in their filenames

Using operators in "find"

I apologise if this had already been covered before. I am learning find on my OSX Mavericks. I am not quite sure I fully understand the operators.
My folder has 4 files:
123.123
123.abc
abc.123
abc.abc
When I try to "OR" -o operator:
find . \( -name "*.123" -o -name "123.*" \) -print
Output is as expected:
./123.123
./123.abc
./abc.123
But when I take away the bracket / parentheses, i.e.:
find . -name "*.123" -o -name "123.*" -print
... only ./123.abc is printed.
Why....??? I really don't understand how the command is being interpreted by the computer here.
My observation is that ./123.abc fits the second argument (123.*), but not the first (*.123). So it seems somehow, the use of -o before the second argument without brackets lead to the first argument behaving as if it had a "NOT" (!) operator.
Taking bracket out makes -print action execute for this condition only:
-name "123.*"
Which prints: ./123.abc
This is a question of operator precedence between the "and" and "or" operators.
find . -name "*.123" -o -name "123.*" -print
is in fact
find . -name "*.123" -o -name "123.*" -a -print
As and has higher priority, this is interpreted as:
find . -name "*.123" -o \( -name "123.*" -a -print \)
If you want your expected behaviour without parenthesis, write that:
sh$ find . -name "*.123" -o -name "123.*"
./abc.123
./123.abc
./123.123
Here the "print" action is implied, and not subject to grouping.
If you want the full-verbose equivalent, you could write:
sh$ find . -name "*.123" -a -print -o -name "123.*" -a -print
./abc.123
./123.abc
./123.123
Which is interpreted as:
find . \( -name "*.123" -a -print \) -o \( -name "123.*" -a -print \)

how to use find command for multiple file names?

I have to search for files with extension *.c or *.cpp or *.cc in a single find command using name. How to use an OR operator in find.?
find . -name '*.c' OR '*.cpp' OR '*.cc'
The exact syntax on how do i use it?
Interestingly
find . -name '*.c' -or -name '*.cpp' -or -name '*.cc'
Group them with \( \) (the slash is just to escape from the shell).
find . \( -iname bla -or -iname foo -or -name 'john dorian' \)

Bash/Shell Combine options using find

Using the find command is there a way to combine options:
i.e.
find . -type fd -name "somefile"
Although -type ignores the second option; I'm looking to find only files or directories.
You can use -o for OR condition in find:
find . \( -type d -o -type f \) -name "somefile"

Find and delete old files excluding some subdirectories

I have been searching for a while, but can't seem to get a succinct solution. I am trying to delete old files but excluding some subdirectories (passed via parm) and their child subdirecories.
The issue that I am having is that when the subdirectory_name is itself older than the informed duration (also passed via parm) the find command is including the subdirectory_name on the list of the find. In reality the remove won't be able to delete these subdirectories because the rm command default option is f.
Here is the find commmand generated by the script:
find /directory/ \( -type f -name '*' -o -type d \
-name subdirectory1 -prune -o -type d -name directory3 \
-prune -o -type d -name subdirectory2 -prune -o \
-type d -name subdirectory3 -prune \) -mtime +60 \
-exec rm {} \; -print
Here is the list of files (and subdirectories brought by the find command)
/directory/subdirectory1 ==> this is a subdreictory name and I'd like to not be included
/directory/subdirectory2 ==> this is a subdreictory name and I'd like to not be included
/directory/subdirectory3 ==> this is a subdreictory name and I'd like to not be included
/directory/subdirectory51/file51
/directory/file1 with spaces
Besides this -- the script works fine not bringing (excluding) the files under these 3 subdirectories:
subdirectory1, subdirectory2 and subdirectory3.
Thank you.
Following command will delete only files older than 1 day.
You can exclude the directories as shown in the example below, directories test1 & test2 will be excluded.
find /path/ -mtime +60 -type d \( -path ./test1 -o -path ./test2 \) -prune -o -type f -print0 | xargs -0 rm -f
Though it would be advisable to see what's going to be deleted using -print
find /path/ -mtime +60 -type d \( -path ./test1 -o -path ./test2 \) -prune -o -type f -print
find /directory/ -type d \(
-name subdirectory1 -o \
-name subdirectory2 -o \
-name subdirectory3 \) -prune -o \
-type f -mtime +60 -print -exec rm -f {} +
Note that the AND operator (-a, implicit between two predicates if not specified) has precedence over the OR one (-o). So the above is like:
find /directory/ \( -type d -a \(
-name subdirectory1 -o \
-name subdirectory2 -o \
-name subdirectory3 \) -a -prune \) -o \
\( -type f -a -mtime +60 -a -print -a -exec rm -f {} + \)
Note that every file name matches the * pattern, so -name '*' is like -true and is of no use.
Using + instead of ; runs fewer rm commands (as few as possible, and each is passed several files to remove).
Do not use that code above on directories writeable by others as it's vulnerable to attacks whereby the attacker can change a directory to a symlink to another one in between the time find traverses the directory and calls rm to have you delete any file on the filesystem. Can be alleviated by changing the -exec part with -delete or -execdir rm -f {} \; if your find supports them.
See also the -path predicate if you want to exclude a specific subdirectory1 instead of any directory whose name is subdirectory1.

Resources