- 论坛徽章:
- 0
|
How do I loop through files with spaces in their name?
So, you're going to loop through a list of files? How is this list
stored? If it's stored as text, there probably was already an
assumption about the characters allowed in a filename. Every
character except '\0' (NUL) is allowed in a file path on Unix. So
the only way to store a list of file names in a file is to
separate them by a '\0' character (if you don't use a quoting
mechanism as for xargs input).
Unfortunately most shells (except zsh) and most standard unix text
utilities (except GNU ones) can't cope with "\0"
characters. Moreover, many tools, like "ls", "find", "grep -l"
output a \n separated list of files. So, if you want to
postprocess this output, the simpler is to assume that the
filenames don't contain newline characters (but beware that once
you make that assumption, you can't pretend anymore your code is
reliable (and thus can't be exploited)).
So, if you've got a newline separated list of files in a
list.txt file, Here are two ways to process it:
1-
while IFS= read -r file <&3; do
something with "$file" # be sure to quote "$file"
done 3< list.txt
(if your read doesn't have the "-r" option, either make another
assumption that filenames don't contain backslashes, or use:
exec 3<&0
sed 's/\\/&&/g' < list.txt |
while IFS= read file; do
something with "$file" <&3 3<&-
done
)
2-
IFS="
" # set the internal field separator to the newline character
# instead of the default "<space><tab><NL>".
set -f # disable filename generation (or make the assumption that
# filenames don't contain *, [ or ? characters (maybe more
# depending on your shell)).
for file in $(cat < list.txt); do
something with "$file" # it's less a problem if you forget to
# quote $file here.
done
Now, beware that there are things you can do before building
this list.txt. There are other ways to store filenames. For
instance, you have the positional parameters.
with:
set -- ./*.txt
you have the list of txt files in the current directory, and no
problem with weird characters. Looping through them is just a
matter of:
for file
do something with "$file"
done
You can also escape the separator. For instance, with
find . -exec sh -c 'printf %s\\n "$1" | sed -n '"':1
\$!{N;b1
}
s/|/|p/g;s/\n/|n/g;p'" '{}' '{}' \;
instead of
find . -print
you have the same list of files except that the \n in filenames
are changed to "|n" and the "|" to "|p". So that you're sure
there's one filename per line and you have to convert back "|n"
to "\n" and "|p" to "|" before referring to the file. |
|