Match .h, .m, .mm files but NOT .html - bash

I want to do some processing on just the source files of type .h, .m, .mm. I do NOT want to include the .html files.
The following misses the .mm files as I'm only matching .h or .m and not trying to catch longer extensions.
find ./ -type f -name "*.[hm]"
This only catches the .mm files, as the ? operator always matches a character. I'd like it to be an optional zero or one match like in regex.
find ./ -type f -name "*.[hm]?"
But if I use * instead, it matches 0 or multiple characters. This returns everything, but also has the .html files that I don't want.
find ./ -type f -name "*.[hm]*"
Any ideas on how to do this?

You can combine conditions:
find -type f -and \( -name '*.h' -or -name '*.m' -or -name '*.mm' \)
Or:
find ./ -type f -and -name '*.[hm]*' -and -not -name '*.html'

You were running OS X. There is -E switch that you can use to tell find that the regular expression is an extended expression.
Interpret regular expressions followed by -regex and -iregex primaries as extended (modern) regular expressions rather than basic regular expressions (BRE's). The re_format(7) manual page fully describes both formats.
Your command should now work:
find -E . -type f -regex ".*\.([hm]|mm)"

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

Bash Find with ignore

I need to find files and ignore files like "^02" (it is regex). If "^02" is directory, then I need to ignore every files, which are inside directory. I don't know how to do it. I tried to use something like.
find . -type f -not -regex "^9" -o -prune
But it doesn't works.
Note that the regex doesn't use ^ and $ as it always has to match the whole string. Moreover, the path starts with ./ if the first argument to find is ., so you need to include it, too.
find -type f -not -regex '\./02.*'
If you want to exclude even subdirectories, use .*/02.* for the regex.
If you want to only exclude the directories matching the pattern, but you want to keep the files, you need to use prune only for directories matching the regex, and -false to remove the directories from the list:
find . -type d -regex '\./02.*' -prune -false -or -type f
Also, you can use patterns instead of regexes for simple cases. That way, you can use -name to include subdirectories:
find . -name '02*' -prune -false -or -type f

In Bash, how do you delete all files with same name, except the one located in a specific folder?

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.

Having trouble with parentheses in unix find and correct syntax

This one liner works, the goal:
search a directory
find all files that are newer than a timestamp file
that are NOT named .DS_Store
otherwise, list all those other files.
I came up with this, which works, but I see examples online that use a lot of parentheses for which I am using none. I was thinking there may be a better way:
find /Users/$USER/Library/Messages/Attachments -not -name ".DS_Store" -not -name "timestamp" -name "*" -type f -newer /Users/$USER/Library/Messages/scripts/timestamp
And ultimately I want to take the results and copy them to a specific place. For that I was going to append this:
-exec cp {} archive_files/ \;
You could combine all the -not expressions into a parenthesized group by applying de Morgan's Law:
-not \( -name .DS_Store -o -name timestamp \)
I don't see the point in your simple case, but if you had lots of names to exclude it might be clearer.

Find multiple file with name (shell unix)

I want to delete all files with names like: *~ or #*#.
I have tried:
find "dir" -name '#*#' -or -name '*~' -delete
but it only deletes files with ~ at the end and not files with # at the beginning and the end
How can I do that?
First, you need to specify a pattern with the -name primary; ## would match a file named exactly ##, while *## would match any file that ends with ##. Second, you need to group the two uses of name so that either one matching will count as a match to be deleted.
find dir \( -name '*##' -or -name '*~' \) -delete
How about find with -regex switch:
find -E . -regex "^./(~|##)$" -exec rm '{}' \;
-E is being used to support extended (modern) regular expression feature.
I find my solutiom : find dir -name "~" -delete -or -name "##" -delete
Thanks everyone

Resources