For loop using find lacks doesn't properly handle directory names having white space character

bash, find, for-loop, whitespace

Solution

cat dirs.txt | while read dir; do
    ls -al "$dir"
done

When you use the backtick operator the output is split into tokens at each whitespace character. `read`, on the other hand, will read one line at a time rather than one word at a time. So the solution, which looks kind of odd, is to pipe the file into the loop so it can be read line by line.

You need to quote `"$dir"` in the `ls` command so that the entire file name, whitespace and all, is treated as a single argument. If you don't do that `ls` will see two file names, `/var/tmp/old` and `files.txt`.

I'm probably going to get awarded the Useless Use of Cat Award. You can simplify this even further by removing the `cat`:

while read dir; do
    ls -al "$dir"
done < dirs.txt

This works because the entire `while` loop acts like one big command, so just like you can pipe `cat` into it, you can also use file redirection.

Taking it even further...

Depending on how `dirs.txt` is generated you may just be able to get rid of it entirely and do everything in one command. If it's just a temporary file you could eliminate it by piping the command generates `dirs.txt` into the `while` loop directly, skipping the temporary file. For instance, replace

find /var -name '*old*' > dirs.txt
while read dir; do
    ls -al "$dir"
done < dirs.txt

with

find /var -name '*old*' | while read dir; do
    ls -al "$dir"
done

Pretend `find` is whatever command you're doing to generate the file list.

And actually, if you are in fact using `find` you can probably do everything in one big `find` command without any loops or anything! For example, the above code using `find` and `while` could be done with a single `find` command like this:

find /var -name '*old*' -exec ls -al {} \;

`find` is a really flexible command that can both search for files matching all kinds of complicated criteria, and pass those files as command-line arguments to other commands using the `-exec` option. When you use `-exec` the `{}` gets replaced with each file name.

Problem

Well i am really stuck at this one. I have dirs.txt which is as follows: ``` /var/tmp/old files.txt /var/tmp/old backups.bak ``` dirs.txt file is generated by the script itself. When i want to use dirs.txt in a for loop like: ``` for dir in `cat dirs.txt` ; do ls -al $dir done ``` Here is what the command throws: ls: cannot access /var/tmp/old: No such file or directory I want the whole filenames to be ls -al'ed but it tries to ls -al /var/tmp/old How may i correct this from the for loop?

Original source