Using many -o in bash find - bash

I am trying to make a small script to find all the video files on my computer.
#!/bin/bash
videos=("avi" "mp4" "mkv" "m4v" "wmv" "ogg" "mov") # etc...
for i in ${videos[#]}
do
find ~ -iname "*.$i"
done
This becomes prohibitively slow, as the program has to loop through the files many times. The only alternate solution I could find was
find ~ -iname "*.avi" -o -iname "*.mp4" -o -iname "*.mkv" # and on and on...
Is there a better, more idiomatic way of doing it?

You could use -regextype egrep -regex, example :
find -regextype egrep -regex '.*\.(avi|mp4|mkv|etc)'

You can use -iregex option:
find . -type f -iregex '.*\.\(avi\|mp4\|mkv\)'

Related

Find for YML and YAML files on bash find

I am trying to find all .yaml and .yml
I tried
find . -name '*.{yml,yaml}' -exec echo "{}" \;
But no results
Neither in the following way
find . -name '*.yml' -name '*.yaml' -exec echo "{}" \;
Returns nothing.
Is it possible to use the find command to search for both extensions?
With GNU find find none or one a:
find . -regextype egrep -regex '.*ya?ml$'
or
find . -regextype egrep -regex '.*ya{0,1}ml$'
See: man find
Something like this.
find . \( -name '*.yaml' -o -name '*.yml' \)
See UsingFind
See Understanding-the-exec-option-of-find

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

Excluding multiple filetypes with find

I have a folder with 20k plus Images and most gui filemanagers (like dolphin) aren't able to manage this amount of data.
So I decided to use the bash instead. My problem is the following:
most of the files are *.IMG or *.LBL files
I am not interested in those files. I look for the others
with find . -type f -not -name "*.LBL" I am able to see all files instead of the *.LBL
with find . -type f -not -name "*.IMG" I am able to see all files instead of the *.IMG
both is not very helpful, since it still fills my terminal
either combining both seems not to work:
find . -type f -not -name "*.LBL" -o -not -name "*.IMG"
What is the correct way to see the files inside a folder excluding multiple filesuffixes?
Group conditions, I think -o -not isn't working as expected. Try this:
find . -type f -not \( -name "*.LBL" -o -name "*.IMG" \)
You can use bash's extended pattern matching (Might have to be turned on in a script with shopt -s extglob; usually enabled by default in an interactive shell):
printf "%s\n" !(*.LBL|*.IMG)

Use find to locate dot files up to 7 characters long

I tried this:
find . \( -name ".?" -o -name ".??" -o -name ".???" -o -name ".????" -o -name ".?????" -o -name ".??????" \)
But it didn't work
You can do it your way, by explicitly putting all the possible combinations. But that is cumbersome when you have many, or when you have to modify it. There are simpler, parameterized ways, to do that.
One way would be using regular expressions:
find . -regextype posix-egrep -regex ".*/\..{1,7}"
If your find does not support regular expressions, another easy way would be to filter the output of find with the help of awk:
find . -name ".*" | awk -F"/" 'length($NF)<8 {print}'
Your command works fine. But there are shorter alternatives. E.g.:
find . -name '.*' ! -name '.???????*'

find option available to omit leading './' in result

I think this is probably a pretty n00ber question but I just gotsta ask it.
When I run:
$ find . -maxdepth 1 -type f \( -name "*.mp3" -o -name "*.ogg" \)
and get:
./01.Adagio - Allegro Vivace.mp3
./03.Allegro Vivace.mp3
./02.Adagio.mp3
./04.Allegro Ma Non Troppo.mp3
why does find prepend a ./ to the file name? I am using this in a script:
fList=()
while read -r -d $'\0'; do
fList+=("$REPLY")
done < <(find . -type f \( -name "*.mp3" -o -name "*.ogg" \) -print0)
fConv "$fList" "$dBaseN"
and I have to use a bit of a hacky-sed-fix at the beginning of a for loop in function 'fConv', accessing the array elements, to remove the leading ./. Is there a find option that would simply omit the leading ./ in the first place?
The ./ at the beginning of the file is the path. The "." means current directory.
You can use "sed" to remove it.
find . -maxdepth 1 -type f \( -name "*.mp3" -o -name "*.ogg" \) | sed 's|./||'
I do not recommend doing this though, since find can search through multiple directories, how would you know if the file found is located in the current directory?
If you ask it to search under /tmp, the results will be on the form /tmp/file:
$ find /tmp
/tmp
/tmp/.X0-lock
/tmp/.com.google.Chrome.cUkZfY
If you ask it to search under . (like you do), the results will be on the form ./file:
$ find .
.
./Documents
./.xmodmap
If you ask it to search through foo.mp3 and bar.ogg, the result will be on the form foo.mp3 and bar.ogg:
$ find *.mp3 *.ogg
click.ogg
slide.ogg
splat.ogg
However, this is just the default. With GNU and other modern finds, you can modify how to print the result. To always print just the last element:
find /foo -printf '%f\0'
If the result is /foo/bar/baz.mp3, this will result in baz.mp3.
To print the path relative to the argument under which it's found, you can use:
find /foo -printf '%P\0'
For /foo/bar/baz.mp3, this will show bar/baz.mp3.
However, you shouldn't be using find at all. This is a job for plain globs, as suggested by R Sahu.
shopt -s nullglob
files=(*.mp3 *.ogg)
echo "Converting ${files[*]}:"
fConv "${files[#]}"
find . -maxdepth 1 -type f \( -name "*.mp3" -o -name "*.ogg" \) -exec basename "{}" \;
Having said that, I think you can use a simpler approach:
for file in *.mp3 *.ogg
do
if [[ -f $file ]]; then
# Use the file
fi
done
If your -maxdepth is 1, you can simply use ls:
$ ls *.mp3 *.ogg
Of course, that will pick up any directory with a *.mp3 or *.ogg suffix, but you probably don't have such a directory anyway.
Another is to munge your results:
$ find . -maxdepth 1 -type f \( -name "*.mp3" -o -name "*.ogg" \) | sed 's#^\./##'
This will remove all ./ prefixes, but not touch other file names. Note the ^ anchor in the substitution command.

Resources