How can I escape white space in a bash loop list?
First, don't do it that way. The best approach is to use find -exec
properly:
# this is safefind test -type d -exec echo '{}' +
The other safe approach is to use NUL-terminated list, though this requires that your find support -print0
:
# this is safewhile IFS= read -r -d '' n; do printf '%q\n' "$n"done < <(find test -mindepth 1 -type d -print0)
You can also populate an array from find, and pass that array later:
# this is safedeclare -a myarraywhile IFS= read -r -d '' n; do myarray+=( "$n" )done < <(find test -mindepth 1 -type d -print0)printf '%q\n' "${myarray[@]}" # printf is an example; use it however you want
If your find doesn't support -print0
, your result is then unsafe -- the below will not behave as desired if files exist containing newlines in their names (which, yes, is legal):
# this is unsafewhile IFS= read -r n; do printf '%q\n' "$n"done < <(find test -mindepth 1 -type d)
If one isn't going to use one of the above, a third approach (less efficient in terms of both time and memory usage, as it reads the entire output of the subprocess before doing word-splitting) is to use an IFS
variable which doesn't contain the space character. Turn off globbing (set -f
) to prevent strings containing glob characters such as []
, *
or ?
from being expanded:
# this is unsafe (but less unsafe than it would be without the following precautions)( IFS=$'\n' # split only on newlines set -f # disable globbing for n in $(find test -mindepth 1 -type d); do printf '%q\n' "$n" done)
Finally, for the command-line parameter case, you should be using arrays if your shell supports them (i.e. it's ksh, bash or zsh):
# this is safefor d in "$@"; do printf '%s\n' "$d"done
will maintain separation. Note that the quoting (and the use of $@
rather than $*
) is important. Arrays can be populated in other ways as well, such as glob expressions:
# this is safeentries=( test/* )for d in "${entries[@]}"; do printf '%s\n' "$d"done
find . -type d | while read file; do echo $file; done
However, doesn't work if the file-name contains newlines. The above is the only solution i know of when you actually want to have the directory name in a variable. If you just want to execute some command, use xargs.
find . -type d -print0 | xargs -0 echo 'The directory is: '
Here is a simple solution which handles tabs and/or whitespaces in the filename. If you have to deal with other strange characters in the filename like newlines, pick another answer.
The test directory
ls -F testBaltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
The code to go into the directories
find test -type d | while read f ; do echo "$f"done
The filename must be quoted ("$f"
) if used as argument. Without quotes, the spaces act as argument separator and multiple arguments are given to the invoked command.
And the output:
test/Baltimoretest/Cherry Hilltest/Edisontest/New York Citytest/Philadelphia