I'm trying to search for files using find, and put those files into a Bash array so that I can do other operations on them (e.g. ls or grep them). But I can't figure out why readarray isn't reading the find output as it's piped into it.

Say I have two files in the current directory, file1.txt and file2.txt. So the find output is as follows:

$ find . -name "file*"
./file1.txt
./file2.txt

So I want to pipe that into an array whose two elements are the strings "./file1.txt" and "./file2.txt" (without quotes, obviously).

I've tried this, among a few other things:

$ declare -a FILES
$ find . -name "file*" | readarray FILES
$ echo "${FILES[@]}"; echo "${#FILES[@]}"

0

As you can see from the echo output, my array is empty.

So what exactly am I doing wrong here? Why is readarray not reading find's output as its standard input and putting those strings into the array?

up vote 10 down vote accepted

When using a pipeline, bash runs the commands in subshells. Therefore, the array is populated, but in a subshell, so the parent shell has no access to it.

Use process substitution:

readarray FILES < <(find)

Note that it doesn't work for files with newlines in their names.

  • Awesome. Thanks for explaining that – villapx Feb 17 '16 at 16:34
  • 2
    In order to support newlines, this is sufficient: readarray -d '' < <(find your_args -print0) – VasyaNovikov Jan 18 at 13:40

The correct solution is:

unset a; declare -a a
while IFS= read -r -u3 -d $'\0' file; do
    a+=( "$file" )        # or however you want to process each file
done 3< <(find /tmp -type f -print0)

That's similar to what Greg's BashFAQ 020 explains in detail and this answer covers.

Has no problem with odd named files (that contain no NUL in the name), with spaces or new lines. And the result is set in an array, which makes it useful for further processing.

  • Great, this is a better solution to the problem I was trying to solve in the first place. +1 as soon as my rep reaches 15 :) – villapx Feb 17 '16 at 19:12

readarray can also read from stdin

readarray FILES <<< "$(find . -name "file*")"; echo "${#FILES[@]}"

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.