Capturing output of find . -print0 into a bash array Capturing output of find . -print0 into a bash array arrays arrays

Capturing output of find . -print0 into a bash array


Shamelessly stolen from Greg's BashFAQ:

unset a iwhile IFS= read -r -d $'\0' file; do    a[i++]="$file"        # or however you want to process each filedone < <(find /tmp -type f -print0)

Note that the redirection construct used here (cmd1 < <(cmd2)) is similar to, but not quite the same as the more usual pipeline (cmd2 | cmd1) -- if the commands are shell builtins (e.g. while), the pipeline version executes them in subshells, and any variables they set (e.g. the array a) are lost when they exit. cmd1 < <(cmd2) only runs cmd2 in a subshell, so the array lives past its construction. Warning: this form of redirection is only available in bash, not even bash in sh-emulation mode; you must start your script with #!/bin/bash.

Also, because the file processing step (in this case, just a[i++]="$file", but you might want to do something fancier directly in the loop) has its input redirected, it cannot use any commands that might read from stdin. To avoid this limitation, I tend to use:

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

...which passes the file list via unit 3, rather than stdin.


Maybe you are looking for xargs:

find . -print0 | xargs -r0 do_something_useful

The option -L 1 could be useful for you too, which makes xargs exec do_something_useful with only 1 file argument.


Since Bash 4.4, the builtin mapfile has the -d switch (to specify a delimiter, similar to the -d switch of the read statement), and the delimiter can be the null byte. Hence, a nice answer to the question in the title

Capturing output of find . -print0 into a bash array

is:

mapfile -d '' ary < <(find . -print0)