2

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.

steeldriver
  • 81,074
xUr
  • 245
  • I'd be interested in why you need the sorting of the results. You cold get this in the 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
  • @Kusalananda Just I'm newer in Linux (more precisely in Ubuntu). if you are telling me that your case is better I will be glad to see it. – xUr Jul 13 '19 at 23:55

2 Answers2

3

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?

steeldriver
  • 81,074
0

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

Kusalananda
  • 333,661
  • Hm... How I understand 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