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

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

Related

Using linux find command to identify files that (A) match either of two names (with wildcards) and (B) that also contain a string

The find command is really useful to identify files with a given name that also contain a string somewhere inside of them.
For instance lets say I'm looking for the string "pacf(" in an R markdown file somewhere in my current directory.
find . -name "*.Rmd" -exec grep -ls "pacf(" {} \;
I get useful results.
However, sometimes, I'm not sure if the file I am looking for is an .R file or a .Rmd file so I might also run.
find . -name "*.R" -exec grep -ls "pacf(" {} \;
And lets say there are no R files containing this string so that returns nothing.
One think I'd like to do is look in both .R and .Rmd files for this string. I would think that I could run
find . -name "*.Rmd" -o -name "*.R" -exec grep -ls "pacf(" {} \
But that returns no results.
However if I run
find . -name "*.R" -o -name "*.Rmd" -exec grep -ls "pacf(" {} \
I get the same results as just searching the .Rmd files. So it seems like it is only running the stuff in exec for the second set of files.
Is there a way I could change these commands to look through both the .R and .Rmd files at once?
Add parentheses '()'
find . \( -name '*.R' -o -name '*.Rmd' \) -exec grep -ls "pacf(" {} \;
you can pass "*[.Rmd]" for -name
like this
find . -name "*[.Rmd]" -exec grep -ls "pacf(" {} \;

Remove content of many subfolders that are names equally

My folder structure looks like this:
|-20200912
-fringe
-other
|-20200915
-fringe
-other
...
And I want to delete the content of all folders called "fringe". How could I achieve this with one command?
I thought about something like this in pseudocode:
find . -name "fringe" -type d -exec rm <content>
You were close.
find . -name 'fringe' -type d -exec rm -rf {} +
This removes every directory named fringe and everything within them.
Single vs double quotes don't make a difference here, but generally prefer single quotes if you mean to pass something in verbatim (with the possible exception of when the thing you want to pass in contains literal single quotes).
If you want to remove the directory's contents, try
... -execdir sh -c 'rm '*' \;
You are looking to process the files and not the directory through rm and so I would use a regex that searches for for files with fringe in the directory path.
find -regex "^.*/fringe/.*$" -type f -exec rm '{}' \;
I would change the rm to ls to ensure that the results are expected before running the actual rm.
You can force gobbing in the exec command:
find -name fringe -type d -execdir sh -c 'rm {}/*' \;

Find all files contained into directory named

I would like to recursively find all files contained into a directory that has name “name1” or name “name2”
for instance:
structure/of/dir/name1/file1.a
structure/of/dir/name1/file2.b
structure/of/dir/name1/file3.c
structure/of/dir/name1/subfolder/file1s.a
structure/of/dir/name1/subfolder/file2s.b
structure/of/dir/name2/file1.a
structure/of/dir/name2/file2.b
structure/of/dir/name2/file3.c
structure/of/dir/name2/subfolder/file1s.a
structure/of/dir/name2/subfolder/file2s.b
structure/of/dir/name3/name1.a ←this should not show up in the result
structure/of/dir/name3/name2.a ←this should not show up in the result
so when I start my magic command the expected output should be this and only this:
structure/of/dir/name1/file1.a
structure/of/dir/name1/file2.b
structure/of/dir/name1/file3.c
structure/of/dir/name2/file1.a
structure/of/dir/name2/file2.b
structure/of/dir/name2/file3.c
I scripted something but it does not work because it search within the files and not only folder names:
for entry in $(find $SEARCH_DIR -type f | grep 'name1\|name2');
do
echo "FileName: $(basename $entry)"
done
If you can use the -regex option, avoiding subfolders with [^/]:
~$ find . -type f -regex ".*name1/[^/]*" -o -regex ".*name2/[^/]*"
./structure/of/dir/name2/file1.a
./structure/of/dir/name2/file3.c
./structure/of/dir/name2/subfolder
./structure/of/dir/name2/file2.b
./structure/of/dir/name1/file1.a
./structure/of/dir/name1/file3.c
./structure/of/dir/name1/file2.b
I'd use -path and -prune for this, since it's standard (unlike -regex which is GNU specific).
find . \( -path "*/name1/*" -o -path "*/name2/*" \) -prune -type f -print
But more importantly, never do for file in $(find...). Use finds -exec or a while read loop instead, depending on what you really need to with the matching files. See UsingFind and BashFAQ 20 for more on how to handle find safely.

How to recursively delete multiple files with different extensions?

I am trying to write a command to remove recursively several files with different extensions (*.extension1, *.extension2 etc) from the current directory and all its related sub-directories.
So far I got this command from another post but I couldn't workout how to adapt it to more than one file in the same command line:
find . -name "*.extension1" -type f -delete
Is it as simple as below?
find . -name "*.extension1";"*.extension2" -type f -delete
Just as a side note, these are all output files that I do not need, but not all are necessarily always output so some of the extensions might not be present. This is just as a general clean-up command.
find . \( -name "*.extension1" -o -name "*.extension2" \) -type f -delete
find Documents ( -name ".py" -o -name ".html" ) -exec file {} \;
OR
find . -regextype posix-egrep -regex ".*\.(extension1|extension2)$" -type f -delete
Just add more options. A regex solution can also apply but this one's better and safer.
find . \( -name '*.ext1' -or -name '*.ext2' \) -type f -delete
Edit: You probably need -or as well. Before deleting, test it first without the -delete option. (2) Added a pair of () as suggested by gniourf_gniourf.
Maybe regexp will help you
find . -regextype posix-awk -regex "(.*.ext1|.*.ext2)" -type f -delete
Another solution using rm:
rm -rf ./**/*.{txt,nfo,jpg,jpeg,png,exe,url}
If you want to delete other files too for e.g. those starting with sample. just add that too:
rm -rf ./**/*.{txt,nfo,jpg,jpeg,png,exe,url} ./**/*/sample.*
This simple command will delete all the files with extension1 and extension2 recursively in that directory.
rm find . -name *.extension1 -o -name *.extentions2

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*" \)

Resources