164

I can use ls -ld */ to list all the directory entries in the current directory. Is there a similarly easy way to just list all the regular files in the current directory? I know I can use find

find . -maxdepth 1 -type f

or stat

stat -c "%F %n" * | grep "regular file" | cut -d' ' -f 3-

but these do not strike me as being overly elegant. Is there a nice short way to list only the regular files (I don't care about devices, pipes, etc.) but not the sub-directories of the current directory? Listing symbolic links as well would be a plus, but is not a necessity.

5
  • 6
    What do you mean by "overly elegant"? As far as I know, the find command is the best way to do what you want. For some reliable other options, you should look into shell specific commands (and those are anything but portable)!
    – rahmu
    Commented Sep 18, 2012 at 9:58
  • 2
    @rahmu I was for something similar to ls -d */, which is short, easy to type, and easy to understand. So I'm pretty happy with Ulrich Dangel's answer, even though I'm not using zsh. Commented Sep 18, 2012 at 10:41
  • ls offers a load of switches like that find doesn't. find is not ls. Commented Jun 4, 2021 at 16:27
  • I found what I was looking for right in your question. -type f Thanks. Commented Jul 20, 2021 at 16:28

9 Answers 9

213
ls -p | grep -v / 

This command lists all non-hidden files that aren't directories (regular files, links, device files, etc.). To also include hidden files, add the -A option to ls

It assumes none of the files have newline characters in their name. Adding a -q option to ls would transform all non-printable characters including newline to ?, guaranteeing they're on one line and so suitable for feeding to a line-based utility like grep and for printing on a terminal.

12
  • 15
    +1, this is a good answer. Down voting a new user with no comment is pretty shocking to me, especially when a working answer is provided. The only edge case I can see where this won't work properly is if there is a newline in a filename. Also retaining colours would be a nice addition where supported. For GNU you could add --color=always to the ls, for OSX -G.
    – Graeme
    Commented Feb 23, 2014 at 10:38
  • Hi, the command you have given is great and it certainly helps me out however I was wondering if it is possible to use the above command and change the file permission at the same time? I tried adding in sudo chmod 777 at the end, and I was given errors... Any help is greatly appreciated
    – dissidia
    Commented Sep 4, 2015 at 2:14
  • 3
    If you also like to exclude symlinks that point to directories do ls -pL | grep -v /. The added -L dereferences symbolic links. Commented Mar 7, 2016 at 18:36
  • 1
    If your also like to use wildcard filter, do ls -pdL something* | grep -v /. The added -d option lists directories themselves, not their contents, so they will be excluded correctly.
    – Rockallite
    Commented Jan 24, 2017 at 2:15
  • 1
    I particularly like this " ls -p | grep -v / " because I can modify to " ls -latrp | grep -v / " to get the full picture, sorted by time. Thx! Commented Dec 15, 2019 at 12:18
41

With zsh and Glob Qualifiers you can easily express it directly, e.g:

echo *(.)

will either only return the list of regular files or an error depending on your configuration.

For the non-directories:

echo *(^/)

(will include symlinks (including to directories), named pipes, devices, sockets, doors...)

echo *(-.)

for regular files and symlinks to regular files.

echo *(-^/)

for non-directories and no symlinks to directories either.

Also, see the D globbing qualifier if you want to include Dot files (hidden files), like *(D-.).

1
  • could you please explain the pattern '*(.)' really quick? '*' matches all but what does '(.)' do? Commented May 28, 2021 at 8:02
28

To list regular files only:

ls -al | grep '^-'

With symbolic links (to any type of file) included:

ls -al | grep '^[-l]'

Where the first character of the list describes the type of file, so - means that it's a regular file, for symbolic link is l.

Debian/Ubuntu

Print the names of the all matching files (including links):

run-parts --list --regex . .

With absolute paths:

run-parts --list --regex . "$PWD"

Print the names of all files in /etc that start with p and end with d:

run-parts --list --regex '^p.*d$' /etc
1
  • 1
    This is the only one correct answer taking in consideration links and FIFOs. Way underated.
    – nethero
    Commented Aug 8, 2020 at 20:36
17

ls has no option to do that, but one of the nice things about unix & linux is that long-winded and inelegant pipelines can easily be turned into a shell script, function, or alias. and these can, in turn, be used in pipelines just like any other program.

(NOTE: there are some scope issues with functions and aliases. Scripts are available to any executable that can read and execute them. Aliases and functions are only available in the current shell - although a sub-shell's .profile/.bashrc etc may redefine them and thus make them available. Also, a script can be written in any language - including bash/sh, awk, perl, python, and others - whichever one is best for the job or that you are most familiar with)

e.g.

alias lsf='find . -maxdepth 1 -type f -print0 | xargs -0r ls'

I've added xargs so that you can use use all the usual ls options, e.g. lsf -lrS

Because it uses find, all of the normally-hidden dotfiles will be displayed, and all of the filenames will be prefixed with ./ - that's about the only difference you'll notice.

You could exclude dot files with ! -iname '.*' but then you'd have to have two versions of the alias - one that displayed dot files and one that didn't.

alias lsf2='find . -maxdepth 1 -type f -a ! -iname '\''.*'\'' -print0 | xargs -0r ls'

Alternatively, if lsf was a script rather than an alias you could parse the options (perhaps with getopts or /usr/bin/getopt or similar), and exclude dotfiles unless -a was present.

1
  • -name is enough, you don't need -iname.
    – Totor
    Commented Jan 2, 2014 at 1:13
9
bash-4.2$ ls -F | grep -v '[/@=|]$' | more

The -F option to ls appends * to executables, / to directories, @ to symbolic links, = to sockets, and | to FIFOs. You can then use grep to exclude the non-regular file characters from output and you have the files. This will work in any shell, not just zsh.

The weaknesses are:

  1. Any file whose name ends in @, = or | will be excluded (but you shouldn't really have files with those characters in the name anyway)
  2. That doesn't exclude device files or some exotic types of files on some systems like doors.
  3. You will have an asterisk appended on any file that is executable. That could be handled by piping through sed to remove any '*' characters from the output.
  4. That doesn't work properly if there are file names containing newline characters.
1
  • On the system I'm writing this on, 10% of the files have names containing =, @ or | characters. = is common in maildir or filenames that have a base64 or quoted-printable encoded part. @ in locale definitions, for email addresses and more. It's rare for them to appear at the end of the file name though. (only 31 out of 2.2 millions on this system for instance) Commented Jul 31, 2016 at 15:14
2

The manual stated the 'f' option is against 'regular file' only, not pipes, socket or block/char devices, which means you're already doing the right stuff.

   -type c
          File is of type c:

          b      block (buffered) special

          c      character (unbuffered) special

          d      directory

          p      named pipe (FIFO)

          f      regular file

          l      symbolic link; this is never true if the -L option or the
                 -follow option is in effect, unless the symbolic link  is
                 broken.  If you want to search for symbolic links when -L
                 is in effect, use -xtype.

          s      socket

          D      door (Solaris)
2

My favourite sollution:

#!/bin/bash
for f in *
do
        if [ -f $f ]; then echo "$f"; fi
done

It can be done as one liner: for f in *; do [ -f $f ] && echo "$f"; done; You can run it in the subshell and cd to the target directory. I would use ls for directories with a lot of files as it is quicker than find in this case (which makes it more elegant in my opinion). You would ideally use printf instead of echo for maximum security.

2
-1

This is not particularly short but you could turn it into a script or alias if you needed to call it quickly. Using the ls command as an input array, call ls -ld on each entry piped to grep to exclude directories with the output sent to null, and if successful echo the original input:

for list in `ls` ; do ls -ld $list | grep -v ^d > /dev/null && echo $list ; done ;

You can invert the grep and conditional output, same results:

for list in `ls` ; do ls -ld $list | grep ^d > /dev/null || echo $list ; done ;
2
-1

This is a bit old thread, but the cleanest way to list files only.

ls -al | grep '^-' | awk '{print $9}'
4
  • 2
    Related: unix.stackexchange.com/questions/128985/why-not-parse-ls
    – Kusalananda
    Commented Jul 27, 2018 at 21:25
  • 1
    touch "some file name" to see a quick counterexample
    – Jeff Schaller
    Commented Jul 28, 2018 at 0:46
  • @JeffSchaller You have a valid point. For that something like this. ls -al | grep '^-' | awk '{ for(i=9; i<=NF; ++i) printf $i""FS; print "" }', should handle the files with spaces in them.
    – R J
    Commented Jul 28, 2018 at 10:34
  • @RJ, not if they are leading or trailing spaces or if there are sequences of one or more spaces. TABs and NLs would also be a problem and because you passing the filename parts are printf's format argument, % would also be a problem. Commented Aug 9, 2020 at 17:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .