How do I correctly define and interpolate this variable into my find command?

I wrote a Bash script, for managing my music files, a part of it creates a list of such files.

all_exts=" -iname "*.webm" -o -iname "*.mkv" "
#all_exts=' -iname "*.webm" -o -iname "*.mp3" -o -iname "*.mp4" -o -iname "*.mkv" ' 
#The line below works fine
#find "${HOME}/pCloudDrive/SocialSciences/Arabic/al_aghani" -maxdepth 1 -type f -size +1M ( -iname "*.webm" -o -iname "*.mp3" -o -iname "*.mp4" -o -iname "*.mkv" )|
find "${HOME}/pCloudDrive/SocialSciences/Arabic/al_aghani" -maxdepth 1 -type f -size +1M ( "$all_exts" )|
awk -v FS="/" '{print $8}' > "${HOME}/pCloudDrive/SocialSciences/Arabic/al_aghani/list"
#it does not work with single quotes either

I get the following output,
find: paths must precede expression: -iname "*.webm" -o -iname "*.mkv" '
When I run the script without trying to interpolate all_exts to my find command the script work fine. How might I correctly expand all_exts?
P.S. This script is simple just for the sake of isolating the problem. I really need to interpolate all_exts into the find command because I have many find commands doing this job in many different directories.

Asked By: John Smith


This is because—since you are quoting the variable, as you should—it is passed aa single string to find. You can see this with set -x:

$ all_exts=" -iname "*.webm" -o -iname "*.mkv" "
$ set -x
$ find .  "$all_exts" > /dev/null 
+ find . ' -iname "*.webm" -o -iname "*.mkv" '

Unfortunately, you can’t just not quote it. First because that can be dangerous so it’s best not to make a habit out of it, and second because it wouldn’t work here anyway since if you don’t quote the expression, the shell will split it and pass it to find as separate, quoted strings:

$ find .  $all_exts
+ find . -iname '"*.webm"' -o -iname '"*.mkv"'

So that would look for files named "foo.webm", where the quotes are part of the file name. The Right Way® of passing such arguments to commands as a variable is to use an array and pass the quoted array to find:

$ all_exts=( '-iname' '*.webm' '-o' '-iname' '*.mkv' )
$ find . ( "${all_exts[@]}" )
+ find . '(' -iname '*.webm' -o -iname '*.mkv' ')'
Answered By: terdon
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.