Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
up vote 6 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.

share|improve this answer
    
Awesome. Thanks for explaining that – villapx Feb 17 '16 at 16:34

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.

share|improve this answer
    
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[@]}"
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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