Capturing output of find . -print0 into a bash array
Solution 1:
Shamelessly stolen from Greg's BashFAQ:
unset a i
while IFS= read -r -d $'\0' file; do
a[i++]="$file" # or however you want to process each file
done < <(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 i
while IFS= read -r -u3 -d $'\0' file; do
a[i++]="$file" # or however you want to process each file
done 3< <(find /tmp -type f -print0)
...which passes the file list via unit 3, rather than stdin.
Solution 2:
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)
Solution 3:
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.
Solution 4:
The main problem is, that the delimiter NUL (\0) is useless here, because it isn't possible to assign IFS a NUL-value. So as good programmers we take care, that the input for our program is something it is able to handle.
First we create a little program, which does this part for us:
#!/bin/bash
printf "%s" "$@" | base64
...and call it base64str (don't forget chmod +x)
Second we can now use a simple and straightforward for-loop:
for i in `find -type f -exec base64str '{}' \;`
do
file="`echo -n "$i" | base64 -d`"
# do something with file
done
So the trick is, that a base64-string has no sign which causes trouble for bash - of course a xxd or something similar can also do the job.
Solution 5:
Yet another way of counting files:
find /DIR -type f -print0 | tr -dc '\0' | wc -c