146

I should echo only names of files or directories with this construction:

ls -Al | while read string
do
...
done

ls -Al output :

drwxr-xr-x  12 s162103  studs         12 march 28 12:49 personal domain
drwxr-xr-x   2 s162103  studs          3 march 28 22:32 public_html
drwxr-xr-x   7 s162103  studs          8 march 28 13:59 WebApplication1

For example if I try:

ls -Al | while read string
do
echo "$string" | awk '{print $9}
done

then output only files and directories without spaces. If file or directory have spaces like "personal domain" it will be only word "personal".

I need very simple solution. Maybe there is better solution than awk.

kenorb
  • 20,250
  • 14
  • 140
  • 164
Alex Zern
  • 1,669
  • 3
  • 13
  • 8

10 Answers10

177

You really should not parse the output of ls. If this is a homework assignment and you are required to, your professor does not know what they're talking about. Why don't you do something like this:

The good...

find ./  -printf "%f\n"

or

for n in *; do printf '%s\n' "$n"; done

...the bad...

If you really really want to use ls, you can make it a little bit more robust by doing something like this:

ls -lA | awk -F':[0-9]* ' '/:/{print $2}'

...and the ugly

If you insist on doing it the wrong, dangerous way and just have to use a while loop, do this:

ls -Al | while IFS= read -r string; do echo "$string" | 
    awk -F':[0-9]* ' '/:/{print $2}'; done

Seriously though, just don't.

terdon
  • 234,489
  • 66
  • 447
  • 667
  • `drwx------ 2 s162103 studs 3 oct. 9 2012 .ssh drwxr-xr-x 3 s162103 studs 6 oct. 25 09:02 .subversion drwxrwxrwt 3 s162103 studs 3 nov. 15 2012 .TempoItems` sometimes i've got date without time, only year and gawk return empty – Alex Zern Mar 30 '13 at 16:30
  • 2
    @AlexZern Well, that's one of the reasons you don't parse the output of `ls`. – terdon Mar 30 '13 at 16:33
  • 2
    @AlexZern why do you need the `-l` switch anyway? If all you want is the file names, just run `ls -A`. Using `-l` just makes your life harder since you now have to parse the output. – terdon Mar 30 '13 at 16:37
  • because I need additional information like access, date, owner,etc. And I wrote a huge script ,which works well, except print file and dir names with spaces. – Alex Zern Mar 30 '13 at 16:46
  • 4
    OK then. What we have here is an [XY problem](http://meta.stackexchange.com/q/66377/203101). `ls` is just not the best way of getting what you need. Why don't you post a new question explaining the information you want to collect and we can suggest ways of doing it. Alternatively, you can use your script as is, and use one of the suggestions above to print the name only. But seriously, don't parse `ls`, just tell us exactly what you are trying to do in a new question. – terdon Mar 30 '13 at 16:49
  • `ls -lA | gawk -F':[0-9]* ' '/:/{print $2}'` is in no way robust (and does not require GNU `awk`). – Stéphane Chazelas Jul 27 '17 at 08:57
  • @StéphaneChazelas it is more robust than the OP's `while` loop. Of course it's not good, that's kind of the whole point of this answer. I removed the `echo`, again, that wasn't the point I was trying to make there but OK, may as well make it safer. As for `gawk`, fair enough. I just used it by default, I wasn't suggesting it was needed. – terdon Jul 27 '17 at 09:00
  • I used this to generate include "file.php" for 70 files in 1 folder... great answer – Mac A. Jan 20 '20 at 23:34
  • Taking a cue from the above - I have used the command: `find -maxdepth 1 -name "abc*" -printf "%f\n"` – Akshay Gehi May 19 '21 at 09:01
171

Is there some reason that ls -A1* won't work?

E.g.:

$ touch file1 file2 file\ with\ spaces
$ ls -Al
total 0
-rw-r--r-- 1 bahamat bahamat 0 Mar 30 22:31 file1
-rw-r--r-- 1 bahamat bahamat 0 Mar 30 22:31 file2
-rw-r--r-- 1 bahamat bahamat 0 Mar 30 22:31 file with spaces
$ ls -A1
file1
file2
file with spaces
$

* Note: that's a capital letter A and the number one.

bahamat
  • 38,658
  • 4
  • 70
  • 103
100

I wonder why no one mentioned this simple command:

ls -a | sort
Ben
  • 1,118
  • 1
  • 7
  • 6
  • 2
    Brilliant! Any idea how piping to `sort` causes multi-column block to be unpivoted into single column? – msciwoj Sep 14 '16 at 10:49
  • @msciwoj it just takes any strings separated by whitespaces and prints them sorted separated by endlines – Ben Sep 14 '16 at 12:32
  • 9
    It works here because `ls` reverts to `ls -1` (single columnd output) when its output doesn't go to a terminal (and disables non-printable character quoting/replacement; @Kusalananda, your `ls` is not POSIX compliant if it still does). `ls` output is already sorted. So piping it to `sort` will at best do nothing, and at worse mangle the output (if file names contain newline characters) or fail for filenames that are not text. `ls -a | cat` would be better. Or even better `ls -A1` as already mentioned (and because you generally do want the replacements/quoting when displayed on a terminal) – Stéphane Chazelas Mar 23 '18 at 11:22
  • 1
    @StéphaneChazelas Noted. `ls` behaves differently when output is not a terminal. – Kusalananda Mar 23 '18 at 11:25
  • it's not the sort command that causes a single column, it's the `ls -a` which can be used by itself – capitalaudience.com Apr 23 '22 at 17:01
8

You cannot parse the output of ls, let alone ls -l because newline, just like space is as valid a character as any in a filename. Also, you'll need to consider symlinks that have an output like ... foo -> bar.

Why would you use -l anyway if you only want the file name?

Just do:

for file in *; do
  ...
done

If you want to include dot files (except . and ..), depending on the shell:

zsh:

for file in *(ND); do
  ...
done

bash:

shopt -s nullglob dotglob
for file in *; do
  ...
done

ksh93:

FIGNORE='@(.|..)'
for file in ~(N)*; do
  ...
done

POSIXly:

for file in .[!.]* ..?* *; do
  [ -e "$file" ] || [ -L "$file" ] || continue
  ...
done
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
3

You can use:

ls -lA | awk '{print $9}' 

It will work if you happen not to have any files with spaces in their names.... otherwise a slightly more convoluted approach may work:

ls -lA | awk '{$1=$2=$3=$4=$5=$6=$7=$8=""; print $0;}
Mohamed
  • 131
  • 2
  • The question does explicitly state "with spaces". Your second one adds whitespace at the start and squeezes sequences of blanks into one space character and won't work for symlinks. – Stéphane Chazelas Mar 29 '22 at 10:40
2

Try (bash, zsh syntax):

find . -type f -print0 | while IFS= read -r -d '' filename; do
  ...
done

This goes recursive and lists only on normal files (i.e. no dirs or symlinks/fifos/devices...).

See also:

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
kenorb
  • 20,250
  • 14
  • 140
  • 164
2
ls -Al | tr -s ' ' | cut -f9- -d' '

compress multiple spaces into single spaces with tr then you can use cut to split on the fields

simpleuser
  • 416
  • 5
  • 19
  • In my case where I knew the full path just not the file name, `cut` worked best. Full command for getting the most recent with version-aware sorting: `ls /workdir/node-* | cut -f 3 -d / | sort -V -r | head -n 1` – Samuel Neff Feb 05 '20 at 20:23
2

Probably the easiest way to print all file names in directory is just:

echo *

Downside - won't print hidden files.

Grzegorz
  • 21
  • 1
  • Depending on the shell, there may be shell options to set to make the pattern expand to hidden names (e.g. `dotglob` in the `bash` shell), or special patterns that does so (like `*(D)` in the `zsh` shell). This is mentioned in [another answer](https://unix.stackexchange.com/a/195542/116858). – Kusalananda Mar 17 '20 at 14:16
2

How about:

for file in * .[!.]*
do
  printf "%s\n" "$file"
done
Scrutinizer
  • 1,142
  • 5
  • 7
-1

You can also do this:

ls -A | xargs -I% echo %

  • 3
    But that doesn't work properly if there are filenames that contain apostrophes, double quotes, backslashes or newlines or start with blank characters. – Stéphane Chazelas Mar 29 '22 at 10:37
  • 3
    @Dick the question is about `ls -Al`, with the `-l` option. If you’re going to use `ls -A`, there’s no need to post-process its output with `xargs` or anything else; `ls -A` already only outputs file names. If you want to have at most one file per line, use `ls -A1`, [as already mentioned](https://unix.stackexchange.com/a/70663/86440). – Stephen Kitt Mar 29 '22 at 12:12