views:

589

answers:

6

I'm trying to list the files in a directory and do something to them in the Mac OS X prompt.

It should go like this: for f in $(ls -1); do echo $f; done

If I have files without spaces in their names (fileA.txt, fileB.txt), the echo works fine. If the files include spaces in their names ("file A.txt", "file B.txt"), I get 4 strings (file, A.txt, file, B.txt).

I've tried quoting the listing command, but it only changed the problem.

If I do this: for f in $(ls -1); do echo $f; done I get: file A.txt\nfile B.txt

(It displays correctly, but it is a single string and I need the 2 lines separated.

+9  A: 
for f in *; do echo "$f"; done

should do what you want. Why are you using ls instead of * ?

In general, dealing with spaces in shell is a PITA. Take a look at the $IFS variable, or better yet at Perl, Ruby, Python, etc.

derobert
If a simple ls -1 is all that's required, then yes: replace it with *. OP might have other requirements though - I always hit this issue when doing things to files in time modified order, for example.
Alabaster Codify
Well, whenever you use ls, you're going to have this issue more or less, because file names can have newlines. You'll need to switch to some other approach (e.g., find/xargs, perl/ruby/python, bash arrays + a lot of work)
derobert
A: 

Check out the manpage for xargs:

it works like this:

ls -1 /tmp/*.jpeg | xargs rm

diclophis
That is at most a small part of the answer - and without a more complete explanation, your answer is of no help.
Jonathan Leffler
+2  A: 

Here's an answer using $IFS as discussed by derobert http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html

duncan
+11  A: 

Step away from ls if at all possible. Use find from the findutils package.

find /target/path -type f -print0 | xargs -0 your_command_here

-print0 will cause find to output the names separated by NUL characters (ASCII zero). The -0 argument to xargs tells it to expect the arguments separated by NUL characters too, so everything will work just fine.

Replace /target/path with the path under which your files are located.

-type f will only locate files. Use -type d for directories, or omit altogether to get both.

Replace your_command_here with the command you'll use to process the file names. (Note: If you run this from a shell using echo for your_command_here you'll get everything on one line - don't get confused by that shell artifact, xargs will do the expected right thing anyway.)

Edit: Alternatively (or if you don't have xargs), you can use the much less efficient

find /target/path -type f -exec your_command_here \{\} \;

\{\} \; is the escape for {} ; which is the placeholder for the currently processed file. find will then invoke your_command_here with {} ; replaced by the file name, and since your_command_here will be launched by find and not by the shell the spaces won't matter.

The second version will be less efficient since find will launch a new process for each and every file found. xargs is smart enough to pipe the commands to a newly launched process if it can figure it's safe to do so. Prefer the xargs version if you have the choice.

Mihai Limbășan
xargs doesn't work so well if you need to run more than one command on a file. Of course, you can use a shell script as the single command.
derobert
True. There's always the -exec argument to find then (although, as you pointed out, most problems can be rephrased using xargs via a separate shell script.)
Mihai Limbășan
A: 

You can pipe the arguments into read. For example, to cat all files in the directory:

ls -1 | while read FILENAME; do cat "$FILENAME"; done

This means you can still use ls, as you have in your question, or any other command that produces $IFS delimited output.

The while loop makes it much easier to do several things to the argument, and makes complex processing more readable in my opinion. A contrived example:

ls -1 | while read FILE
do
    echo 1: "$FILE"
    echo 2: "$FILE"
done
Alabaster Codify
That helps if there are just spaces (and tabs); it doesn't help if there are newlines in the names. find -print0 and xargs -0 does help in that situation.
Jonathan Leffler
A: 

look --quoting-style option. for instance, --quoting-style=c would produce :

$ ls --quoting-style=c

"file1" "file2" "dir one"

Aif