I want to do this: readarray var1 < <(find "/path/to/dir" \( $var2 \) | sort)
echo $var2
user@domain:~# -name "*.avi" -o -name "*.mkv"
Unfortunately, my script does not work. Please, tell me where I do a mistake.
I want to do this: readarray var1 < <(find "/path/to/dir" \( $var2 \) | sort)
echo $var2
user@domain:~# -name "*.avi" -o -name "*.mkv"
Unfortunately, my script does not work. Please, tell me where I do a mistake.
Use an array:
var2=( -name "*.avi" -o -name "*.mkv" )
readarray var1 < <(find "/path/to/dir" \( "${var2[@]}" \) | sort)
See the somewhat related How can we run a command stored in a variable?
var1
, so it's not like you could port this code to a shell that doesn't support arrays
– steeldriver
Jul 13 '19 at 23:38
To get all names beneath the path /path/to/dir
as a sorted list in bash
:
shopt -s globstar nullglob dotglob
names=( /path/to/dir/** )
The globstar
shell option in bash
enables the use of the **
glob, which works similarly as *
but matches across /
in pathnames (i.e., it matches "recursively" down into directories). The nullglob
shell option makes it so that if a shell glob does not match any name, it is removed completely (the default is to keep the unexpanded pattern). The dotglob
option causes hidden filenames to be matched by default.
The resulting expansion of a shell glob is lexicographically ordered, as is the default output of sort
, so no piping of names to sort
is necessary.
To get the names that has the filename suffixes .avi
or .mkv
, use
shopt -s globstar nullglob dotglob extglob
names=( /path/to/dir/**/*.@(avi|mkv) )
Here, I've added the extglob
shell option, which allows for using extended globbing pattern. The pattern @(avi|mkv)
means "either avi
or mkv
", so the full pattern would match any name beneath /path/to/dir
that ends with either .avi
or .mkv
. The resulting array would be sorted.
find
would be useful if you need to do things with the found names, or if you need to incorporate specific filetype tests. For example, if you need to make sure you're only matching only regular files, use
find /path/to/dir -type f \( -name '*.avi' -o -name '*.mkv' \)
Given the two filename suffixes avi
and mkv
in an array called suffix
, the -name
tests can be constructed as an array:
suffix=( avi mkv )
nametest=( -false ) # assumes GNU find here
for suf in "${suffix[@]}"; do
nametest+=( -o -name ".$suf" )
done
find /path/to/dir -type f \( "${nametest[@]}" \)
It is almost never correct to save the output of find
in a variable when you actually want to do things to the found names. Instead, use -exec
to carry out the action:
find /path/to/dir -type f \( "${nametest[@]}" \) -exec sh -c '
for pathname do
# do something with "$pathname" here
done' sh {} +
For further reading, see
names=( /path/to/dir/**/*.@(avi|mkv) )
First part is for zsh
? And really, It is easier, but can your case be more dynamic? I mean names=( /path/to/dir/**/*.@(avi|mkv) )
. Can I use a variable type of array (or another) instead @(avi|mkv)
? Just I save suffixes to variable and use in another part of code too.
– xUr
Jul 14 '19 at 08:26
zsh
shell in a much more convenient and safe way which would not rely on the pathnames being sane (containing no newlines). – Kusalananda Jul 13 '19 at 23:38