139

I want to iterate over each line in the output of: ls -l /some/dir/*

Right now I'm trying: for x in $(ls -l $1); do echo $x; done

However, this iterates over each element in the line separately, so I get:

-r--r-----
1
ivanevf
eng
1074
Apr
22
13:07
File1

-r--r-----
1
ivanevf
eng
1074
Apr
22
13:17
File2

But I want to iterate over each line as a whole, though. How do I do that?

Damian T.
  • 321
  • 1
  • 10
Ivan
  • 2,314
  • 3
  • 18
  • 22

6 Answers6

280

Set IFS to newline, like this:

IFS='
'
for x in `ls -l $1`; do echo $x; done

Put a sub-shell around it if you don't want to set IFS permanently:

(IFS='
'
for x in `ls -l $1`; do echo $x; done)

Or use while | read instead:

ls -l $1 | while read x; do echo $x; done

One more option, which runs the while/read at the same shell level:

while read x; do echo $x; done << EOF
$(ls -l $1)
EOF
Randy Proctor
  • 7,396
  • 3
  • 25
  • 26
  • 15
    Beware when piping to while, the while will run in a subshell, which means it could not change variables in the main process – Reorx Jan 14 '16 at 03:42
  • 1
    What is the $1 doing here? I need to use this in a script I'm writing that accepts arguments. The $1 is throwing things off. What can I put there instead of $1 to get the same effect? I'd like to use the second solution. – noob-in-need Apr 14 '16 at 15:33
  • @noob-in-need Replace it with whatever directory you want to list – vpzomtrrfrt May 05 '16 at 00:44
  • 3
    You might want to do `read -r` or else bash will strip unescaped backslashes from the data. – Rag Apr 18 '17 at 18:34
7

It depends what you want to do with each line. awk is a useful utility for this type of processing. Example:

 ls -l | awk '{print $9, $5}'

.. on my system prints the name and size of each item in the directory.

Adam Holmberg
  • 7,245
  • 3
  • 30
  • 53
3

As already mentioned, awk is the right tool for this. If you don't want to use awk, instead of parsing output of "ls -l" line by line, you could iterate over all files and do an "ls -l" for each individual file like this:

for x in * ; do echo `ls -ld $x` ; done
Axel
  • 13,939
  • 5
  • 50
  • 79
3

You can also try the find command. If you only want files in the current directory:

find . -d 1 -prune -ls

Run a command on each of them?

find . -d 1 -prune -exec echo {} \;

Count lines, but only in files?

find . -d 1 -prune -type f -exec wc -l {} \;

David Koski
  • 965
  • 7
  • 12
  • find is broken. Ioften get some bizarro error messages. it wants to give me help when the commandline is perfectly valid, and it seems like once it starts doing this, it won't stop until next reboot MAYBE. it sometimes seems random in nature concerning errors. not sure how I can report the bug. – Jim Michaels May 20 '14 at 10:37
1

The read(1) utility along with output redirection of the ls(1) command will do what you want.

Steve Emmerson
  • 7,702
  • 5
  • 33
  • 59
0

So, why didn't anybody suggest just using options that eliminate the parts he doesn't want to process.

On modern Debian you just get your file with:

ls --format=single-column 

Further more, you don't have to pay attention to what directory you are running it in if you use the full directory:

ls --format=single-column /root/dir/starting/point/to/target/dir/

This last command I am using the above and I get the following output:

bot@dev:~/downloaded/Daily# ls --format=single-column /home/bot/downloaded/Daily/*.gz
/home/bot/downloaded/Daily/Liq_DailyManifest_V3_US_20141119_IENT1.txt.gz
/home/bot/downloaded/Daily/Liq_DailyManifest_V3_US_20141120_IENT1.txt.gz
/home/bot/downloaded/Daily/Liq_DailyManifest_V3_US_20141121_IENT1.txt.gz
BradChesney79
  • 650
  • 7
  • 16
  • This was helpful. For those on macos: if you've `brew install`'d the coreutils package, then you'll have GNU's ls on your shell with the name `gls` which has the `--format` functionality: `gls --format=single-column` – Chris Sep 28 '20 at 08:14