Concatenate 2 parameters in a find expression - linux

I want to write a command that will display all .c and .cpp files from my computer.
I know that I can use find with -name but how can I concatenate the parms to find both file extensions.
Right now I have:
find -name "*.cpp"

Alternative solution
find -regex '.*\.\(c\|cpp\)'
In that way you can avoid multiple -o logic condition (as you requested in HerrSerker answer

Use an OR expression with the -o symbol:
find . -name "*.c" -o -name "*.cpp"
Depending on your system, you may need to escape the dot \. to avoid it matching any character -> -name "*\.cpp".

I guess
find -name "*.cpp" -o -name "*.c"
The -o meaning LOGICAL OR

Related

Using find to find files WITH a certain pattern and not other pattern

I want to use find to find files with _101_ in the name and not .jpg or .wsq extensions, but I cannot get this to work.
I tried things like this:
find . -type f -name '*_101_*' -o -not -name *.jpg -o -name *.wsq
but it doesn't work.
What am I doing wrong?
Your attempt does "matches _101_, or does not end in .jpg, or does not end in .wsq". That'll match every single file, based on the two extensions alone, as a file can only have one.
You have to group differently:
find . -type f -name '*_101_*' -not -name '*.jpg' -not -name '*.wsq'
This applies all rules (logical AND is implied).
You also should quote the parameters to -name, or they might be expanded by the shell instead of find.
You need to use parenthesis (or #Benjamin's solution)
otherwise a or not b or c is evaluated as (a or not b) or c.
And you need and instead of or to filter only files that satisfy both conditions (pass both tests). a and not (b or c)
find -name '*_101_*' -not \( -name '*.jpg' -o -name '*.wsq' \)

Recursively find files with a specific extension

I'm trying to find files with specific extensions.
For example, I want to find all .pdf and .jpg files that's named Robert
I know I can do this command
$ find . -name '*.h' -o -name '*.cpp'
but I need to specify the name of the file itself besides the extensions.
I just want to see if there's a possible way to avoid writing the file name again and over again
Thank you !
My preference:
find . -name '*.jpg' -o -name '*.png' -print | grep Robert
Using find's -regex argument:
find . -regex '.*/Robert\.\(h\|cpp\)$'
Or just using -name:
find . -name 'Robert.*' -a \( -name '*.cpp' -o -name '*.h' \)
find -name "*Robert*" \( -name "*.pdf" -o -name "*.jpg" \)
The -o repreents an OR condition and you can add as many as you wish within the braces. So this says to find all files containing the word "Robert" anywhere in their names and whose names end in either "pdf" or "jpg".
As an alternative to using -regex option on find, since the question is labeled bash, you can use the brace expansion mechanism:
eval find . -false "-o -name Robert".{jpg,pdf}
This q/a shows how to use find with regular expression: How to use regex with find command?
Pattern could be something like
'^Robert\\.\\(h|cgg\\)$'
As a script you can use:
find "${2:-.}" -iregex ".*${1:-Robert}\.\(h\|cpp\)$" -print
save it as findcc
chmod 755 findcc
and use it as
findcc [name] [[search_direcory]]
e.g.
findcc # default name 'Robert' and directory .
findcc Joe # default directory '.'
findcc Joe /somewhere # no defaults
note you cant use
findcc /some/where #eg without the name...
also as alternative, you can use
find "$1" -print | grep "$#"
and
findcc directory grep_options
like
findcc . -P '/Robert\.(h|cpp)$'
Using bash globbing (if find is not a must)
ls Robert.{pdf,jpg}
Recurisvely with ls: (-al for include hidden folders)
ftype="jpg"
ls -1R *.${ftype} 2> /dev/null
For finding the files in system using the files database:
locate -e --regex "\.(h|cpp)$"
Make sure locate package is installed i.e. mlocate

How do I list the files having particular strings from group of directories in bash?

I want to list files having EXACT strings like "hello", "how" and "todo" from a directory (which is having multiple directories). Also I want to list c(.c) and cpp (.cpp) files only.
I have tried with grep -R (grep -R "hello" /home) but not satisfied. Please help me to enhance my grep -R command or any alternate way. Thanks in advance.
if you want to find files, a good start is usually to use find.
if you want to find all .cpp and .-c files that contain the strings "hello", "how" or "todo" in their content, use something like:
find /home \( -name "*.c" -or -name "*.cpp" \) \
-exec egrep -l "(hello|how|todo)" \{\} \;
if instead you want to find all .cpp and .-c files that contain the strings "hello", "how" or "todo" in their filenames, use something like:
find /home \
\( \( -name "*.c" -or -name "*.cpp" \) \
-and \
\( -name "*hello*" -or -name "*how*" -or -name "*todo*" \) \
\)
there is a bit of quoting (using \) involved, as (), {} and ; are considered special characters by the shell...
In fact grep itself would be fine for this.
However I would strongly suggest ack-grep. It is a good alternative to grep which just suit your need.
You can find it here
With ack-grep it is just as simple as
ack-grep --cc --cpp "(hello|how|todo)"
You can try the followings:
grep -rn --include={*.c,*.cpp} yourdirectory -e ^h[a-z]*
This will search through all the files which have .c and .cpp extensions and finds patterns starts with h (you need to prepare you own to meet your need) from your specified directory.

What is wrong with my find command usage?

I'm trying to find all files whose name matches certain C++ file extensions but exclude certain directories matching a pattern with this:
find /home/palchan/code -name "*.[CcHh]" -o -name "*.cpp" -o -name "*.hpp" -a ! -name "*pattern*"
and this still gives me as output certain files like:
/home/palchan/code/libFox/pattern/hdr/fox/RedFox.H
which has the pattern in it?
Here is an example:
> ls -R .
.:
libFox
./libFox:
RedFox.C RedFox.H pattern
./libFox/pattern:
RedFox.C RedFox.H
and then I run:
> find . \( -name "*.[HC]" -a ! -name "*pattern*" \)
./libFox/pattern/RedFox.C
./libFox/pattern/RedFox.H
./libFox/RedFox.C
./libFox/RedFox.H
The following should work:
find /home/palchan/code \( -name "*pattern*" \) -prune -o -type f \( -name "*.[CcHh]" -o -name "*.cpp" -o -name "*.hpp" \) -print
From man find:
-name pattern
Base of file name (the path with the leading directories removed) matches shell pattern pattern. The metacharacters (`*', `?', and `[]') match
a `.' at the start of the base name (this is a change in findutils-4.2.2; see section STANDARDS CONFORMANCE below). To ignore a directory and
the files under it, use -prune; see an example in the description of -path. Braces are not recognised as being special, despite the fact that
some shells including Bash imbue braces with a special meaning in shell patterns. The filename matching is performed with the use of the
fnmatch(3) library function. Don't forget to enclose the pattern in quotes in order to protect it from expansion by the shell.
So, basically, you should use -prune to exclude directories instead of ! -name something
Try doing this :
find /home/palchan/code \( -name "*.[CcHh]" -o -name "*.cpp" -o -name "*.hpp" -a ! -name "*pattern*" \)

find -name "*.xyz" -o -name "*.abc" -exec to Execute on all found files, not just the last suffix specified

I'm trying to run
find ./ -name "*.xyz" -o -name "*.abc" -exec cp {} /path/i/want/to/copy/to
In reality it's a larger list of name extensions but I don't know that matters for this example. Basically I'd like to copy all those found to another /path/i/want/to/copy/to. However it seems to only be executing the last -name test in the list.
If I remove the -exec portion all the variations of files I expect to be found are printed out.
How do I get it to pass the full complement of files found to -exec?
find works by evaluating the expressions you give it until it can determine the truth value (true or false) of the entire expression. In your case, you're essentially doing the following, since by default it ANDs the expressions together.
-name "*.xyz" OR ( -name "*.abc" AND -exec ... )
Quoth the man page:
GNU find searches
the directory tree rooted at each given file name by evaluating the
given expression from left to right, according to the rules of precedence (see section OPERATORS), until the outcome is known (the left
hand side is false for and operations, true for or), at which point
find moves on to the next file name.
That means that if the name matches *.xyz, it won't even try to check the latter -name test or -exec, since it's already true.
What you want to do is enforce precedence, which you can do with parentheses. Annoyingly, you also need to use backslashes to escape them on the shell:
find ./ \( -name "*.xyz" -o -name "*.abc" \) -exec cp {} /path/i/want/to/copy/to \;
More usable than Jaypal's solution would maybe be:
find ./ -regex ".*\.\(jpg\|png\)" -exec cp {} /path/to
find . \( -name "*.xyz" -o -name "*.abc" \) -exec cp {} /path/i/want/to/copy/to \;
It may work:
find ./ -name "*.{xyz,abc}" -exec cp {} /path/i/want/to/copy/to

Resources