- If you really need to capture all file paths in an array up front (assumes
bash
, primarily due to use of arrays and process substitution (<(...)
)[1]; a POSIX-compliant solution would be more cumbersome[2]; also note that this is a line-based solution, so it won't handle filenames with embedded newlines correctly, but that's very rare in practice):
# Read matches into array `vari` - safely: no word splitting, no
# globbing. The only caveat is that filenames with *embedded* newlines
# won't be handled correctly, but that's rarely a concern.
# bash 4+:
readarray -t vari < <(find . -name "multi word file.xml")
# bash 3:
IFS=$'\n' read -r -d '' -a vari < <(find . -name "multi word file.xml")
# Invoke `rm` with all array elements:
rm "${vari[@]}" # !! The double quotes are crucial.
- Otherwise, let
find
perform the deletion directly (these solutions also handle filenames with embedded newlines correctly):
find . -name "multi word file.xml" -delete
# If your `find` implementation doesn't support `-delete`:
find . -name "multi word file.xml" -exec rm {} +
As for what you tried:
vari=`find -name "multi word file.xml"`
(I've removed the spaces around =
, which would result in a syntax error) does not create an array; such a command substitution returns the stdout output from the enclosed command as a single string (with trailing newlines stripped).
- By enclosing the command substitution in
( ... )
, you could create an array:
vari=( `find -name "multi word file.xml"` )
,
but that would perform word splitting on the find
's output and not properly preserve filenames with spaces.
- While this could be addressed with
IFS=$'\n'
so as to only split at line boundaries, the resulting tokens are still subject to pathname expansion (globbing), which can inadvertently alter the file paths.
- While this could also be addressed with a shell option, you now have 2 settings you need to perform ahead of time and restore to their original value; thus, using
readarray
or read
as demonstrated above is the simpler choice.
Even if you did manage to collect the file paths correctly in $vari
as an array, referencing that array as ${vari[@]}
- without double quotes - would break, because the resulting strings are again subject to word splitting, and also pathname expansion (globbing).
- To safely expand an array to its elements without any interpretation of its elements, double-quote it:
"${vari[@]}"
[1]
Process substitution rather than a pipeline is used so as to ensure that readarray
/ read
is executed in the current shell rather than in a subshell.
As eckes points out in a comment, if you were to try find ... | IFS=$'\n' read ...
instead, read
would run in a subshell, which means that the variables it creates will disappear (go out of scope) when the command returns and cannot be used later.
[2]
The POSIX shell spec. supports neither arrays nor process substitution (nor readarray
, nor any read
options other than -r
); you'd have to implement line-by-line processing as follows:
while IFS='
' read -r vari; do
pv vari
done <<EOF
$(find . -name "multi word file.xml")
EOF
Note the require actual newline between IFS='
and '
in order to assign a newline, given that the $'\n'
syntax is not available.