Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capturing output of find . -print0 into a bash array

Using find . -print0 seems to be the only safe way of obtaining a list of files in bash due to the possibility of filenames containing spaces, newlines, quotation marks etc.

However, I'm having a hard time actually making find's output useful within bash or with other command line utilities. The only way I have managed to make use of the output is by piping it to perl, and changing perl's IFS to null:

find . -print0 | perl -e '$/="\0"; @files=<>; print $#files;' 

This example prints the number of files found, avoiding the danger of newlines in filenames corrupting the count, as would occur with:

find . | wc -l 

As most command line programs do not support null-delimited input, I figure the best thing would be to capture the output of find . -print0 in a bash array, like I have done in the perl snippet above, and then continue with the task, whatever it may be.

How can I do this?

This doesn't work:

find . -print0 | ( IFS=$'\0' ; array=( $( cat ) ) ; echo ${#array[@]} ) 

A much more general question might be: How can I do useful things with lists of files in bash?

like image 846
Idris Avatar asked Jul 12 '09 21:07

Idris


People also ask

How can I store the find command results as an array in bash?

In bash, $(<any_shell_cmd>) helps to run a command and capture the output. Passing this to IFS with \n as delimiter helps to convert that to an array. Benjamin W. This will get only the first file of the results of find into the array.

How do you store the output of a command in an array?

to store the output of command <command> into the array my_array . Now the length is stored in my_array_length . What if the output of $(command) has spaces and multiple lines with spaces? I added "$(command)" and it places all output from all lines into the first [0] element of the array.

How do you store output of ls in a variable?

To store the output of a command in a variable, you can use the shell command substitution feature in the forms below: variable_name=$(command) variable_name=$(command [option ...] arg1 arg2 ...) OR variable_name='command' variable_name='command [option ...]

What is $@ in bash?

bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc. Place variables in quotes if the values might have spaces in them.


1 Answers

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.

like image 137
Gordon Davisson Avatar answered Oct 13 '22 00:10

Gordon Davisson