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.

daniel kullmann
  • 9,527
  • 11
  • 39
  • 46

9 Answers9

212
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.

Athul Biju
  • 2,121
  • 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 Feb 23 '14 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 Sep 04 '15 at 02:14
  • 3
    If you also like to exclude symlinks that point to directories do ls -pL | grep -v /. The added -L dereferences symbolic links. – Matthias Braun Mar 07 '16 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 Jan 24 '17 at 02:15
  • And if you wanted to grab all files without folders just remove the -v – jasonleonhard Sep 10 '17 at 23:34
  • for colored output, this might be useful https://stackoverflow.com/a/868086/3779853 – phil294 Oct 14 '17 at 17:19
  • @jasonleonhard this is wrong: if you want to get all folders without files remove -v.. – Timo Dec 23 '17 at 08:08
  • +1, v flag inverts the grep and p is short for --indicator-style=slash which adds a slash to a dir. – Timo Dec 23 '17 at 08:12
  • this didnt help for me because i wanted to size compare each – Brian Thomas Nov 16 '19 at 00:00
  • 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! – opinion_no9 Dec 15 '19 at 12:18
  • This is not a good answer really cause the OP makes the assumption that the directory contains either a file or directory. What about FIFO ending with | and symbolic links ending in @ they are also going to be listed and I wouldn't consider them regular files. I also think that the --file-type option would be more informative. – nethero Aug 08 '20 at 20:35
  • Maybe add a $ to check only at end of line. So: ls -p | grep -v "/$". – Bálint Sass Jan 23 '23 at 11: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-.).

Ulrich Dangel
  • 25,369
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
kenorb
  • 20,988
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.

cas
  • 78,579
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.
kurtm
  • 7,055
  • 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) – Stéphane Chazelas Jul 31 '16 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)
daisy
  • 54,555
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.

nethero
  • 164
-1

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

ls -al | grep '^-' | awk '{print $9}'
R J
  • 852
  • 2
    Related: https://unix.stackexchange.com/questions/128985/why-not-parse-ls – Kusalananda Jul 27 '18 at 21:25
  • 1
    touch "some file name" to see a quick counterexample – Jeff Schaller Jul 28 '18 at 00: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 Jul 28 '18 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. – Stéphane Chazelas Aug 09 '20 at 17:01
-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 ;
Don R
  • 17