Files and directories
ls
List the contents of directories, or the details of individual files.
ls [OPTION...] [FILE...]
ls lists the entries of a directory (the current one if you give no argument). Given a file
instead of a directory, it lists just that file. By default it hides names that start with a dot
and sorts alphabetically; nearly every option changes either what is shown (-a, -d, -R)
or how it is sorted and formatted (-l, -t, -S, -h, -1).
When its output goes to a terminal, ls arranges names in columns. When it goes to a pipe or a
file it prints one name per line instead, which is why ls | wc -l counts entries. That does not
make ls output safe to parse: a filename can contain spaces or even newlines. In scripts, loop
over a glob (for f in *.txt) or use find with -print0.
For the metadata of a single file in a fixed format, stat is the better tool; for a picture of
a whole directory tree, tree.
Examples
List the current directory
ls
README.md access.log data file with spaces.txt link-to-notes logs notes.txt photos scripts src todo.txt
Names are sorted alphabetically and dotfiles such as .env are hidden. Because the output
here is not a terminal, ls prints one name per line; in an interactive shell you would see
columns. Note that file with spaces.txt is a single entry, which is why parsing this text
word by word goes wrong.
Show permissions, owner, size and date
ls -l
total 6 -rw-rw-r-- 1 dev dev 230 Oct 24 02:38 README.md -rw-rw-r-- 1 dev dev 2309 Oct 24 21:55 access.log drwxrwxr-x 2 dev dev 13 Oct 25 01:33 data -rw-rw-r-- 1 dev dev 34 Oct 24 09:47 file with spaces.txt lrwxrwxrwx 1 dev dev 9 Oct 25 02:48 link-to-notes -> notes.txt drwxrwxr-x 2 dev dev 5 Oct 25 17:10 logs -rw-rw-r-- 1 dev dev 190 Oct 25 05:30 notes.txt drwxrwxr-x 2 dev dev 5 Oct 25 13:50 photos drwxrwxr-x 2 dev dev 4 Oct 25 21:27 scripts drwxrwxr-x 3 dev dev 7 Oct 26 01:56 src -rw-rw-r-- 1 dev dev 62 Oct 24 21:28 todo.txt
Each line is: file type and permission bits, link count, owner, group, size in bytes,
modification time, and name. A leading d marks a directory, l a symlink (shown with
-> target). total is the disk space used by the listed files, in 1 KiB blocks.
Include hidden files
ls -la
total 15 drwxrwxr-x 7 dev dev 14 Oct 29 09:26 . drwx------ 1 dev dev 7 Oct 31 20:20 .. -rw-rw-r-- 1 dev dev 42 Oct 24 09:33 .env -rw-rw-r-- 1 dev dev 230 Oct 24 02:38 README.md -rw-rw-r-- 1 dev dev 2309 Oct 24 21:55 access.log drwxrwxr-x 2 dev dev 13 Oct 25 01:33 data -rw-rw-r-- 1 dev dev 34 Oct 24 09:47 file with spaces.txt lrwxrwxrwx 1 dev dev 9 Oct 25 02:48 link-to-notes -> notes.txt drwxrwxr-x 2 dev dev 5 Oct 25 17:10 logs -rw-rw-r-- 1 dev dev 190 Oct 25 05:30 notes.txt drwxrwxr-x 2 dev dev 5 Oct 25 13:50 photos drwxrwxr-x 2 dev dev 4 Oct 25 21:27 scripts drwxrwxr-x 3 dev dev 7 Oct 26 01:56 src -rw-rw-r-- 1 dev dev 62 Oct 24 21:28 todo.txt
-a shows names starting with a dot, including . (this directory) and .. (its parent).
-A is the same without those two, which is usually what you want in scripts.
Human-readable sizes
ls -lh data
total 5.5K -rw-rw-r-- 1 dev dev 244 Oct 25 21:53 config.json -rw-rw-r-- 1 dev dev 66 Oct 25 17:04 fruits.txt -rw-rw-r-- 1 dev dev 65 Oct 25 17:38 hosts.txt -rw-rw-r-- 1 dev dev 59 Oct 26 08:21 inventory.tsv -rw-rw-r-- 1 dev dev 27 Oct 26 09:05 numbers.txt -rw-rw-r-- 1 dev dev 78 Oct 26 20:12 poem.txt -rw-rw-r-- 1 dev dev 405 Oct 26 08:37 sales.csv -rw-rw-r-- 1 dev dev 503 Oct 26 05:19 servers.json -rw-rw-r-- 1 dev dev 149 Oct 26 17:44 settings.conf -rw-rw-r-- 1 dev dev 436 Oct 27 08:26 users.csv -rw-rw-r-- 1 dev dev 20 Oct 26 18:06 windows.txt
-h prints sizes with a unit suffix (K, M, G) once they reach 1024 bytes. Every file in
data/ is under 1 KiB, so only the total line gets a suffix; on a directory of logs or
images you would see 4.0K, 12M and so on.
Newest files first
ls -lt logs
total 2 -rw-rw-r-- 1 dev dev 150 Oct 27 13:12 app.log.1 -rw-rw-r-- 1 dev dev 729 Oct 27 03:10 app.log -rw-rw-r-- 1 dev dev 112 Oct 27 00:38 error.log
-t sorts by modification time, newest first. Add -r to reverse the order, so the most
recently changed file ends up at the bottom, right above your prompt: ls -ltr.
Largest files first
ls -lS photos
total 2 -rw-rw-r-- 1 dev dev 2315 Oct 28 05:43 forest.png -rw-rw-r-- 1 dev dev 1587 Oct 27 18:41 beach.png -rw-rw-r-- 1 dev dev 583 Oct 27 04:29 IMG_2041.png
-S sorts by size, largest first. Combine with -r to put the smallest first.
Show a directory itself, not its contents
ls -ld data src
drwxrwxr-x 2 dev dev 13 Oct 25 01:33 data drwxrwxr-x 3 dev dev 7 Oct 26 01:56 src
Without -d, ls -l data lists what is inside data. With -d you get one line for the
directory itself, which is how you check its own permissions and owner.
Mark directories, executables and links
ls -F src
Makefile app.py* lib/ main.c util.sh*
-F appends a marker to each name: / for directories, * for executables (app.py and
util.sh have the execute bit), and @ for symbolic links, as link-to-notes@ would show in
the parent directory. It tells types apart without relying on color.
List subdirectories recursively
ls -R src
src: Makefile app.py lib main.c util.sh src/lib: helpers.py
List only files matching a pattern
ls -l data/*.csv
-rw-rw-r-- 1 dev dev 405 Oct 26 08:37 data/sales.csv -rw-rw-r-- 1 dev dev 436 Oct 27 08:26 data/users.csv
The shell expands data/*.csv before ls runs, so ls simply receives two file names and
lists those. ls itself does not understand wildcards.
Full, sortable timestamps
ls -l --time-style=long-iso logs
total 2 -rw-rw-r-- 1 dev dev 729 2026-10-27 03:10 app.log -rw-rw-r-- 1 dev dev 150 2026-10-27 13:12 app.log.1 -rw-rw-r-- 1 dev dev 112 2026-10-27 00:38 error.log
The default date format drops the year for recent files and the time for old ones.
--time-style=long-iso always prints YYYY-MM-DD HH:MM, and full-iso adds seconds,
fractions and the time zone.
Count the entries in a directory
ls -A data | wc -l
11
When ls writes to a pipe it puts one name per line (the same as -1), so wc -l
counts entries. -A includes dotfiles but not . and ... Names with newlines would be
counted twice, a rare but real reason to prefer find in scripts.
What happens when a file does not exist
ls notes.txt missing.txt
ls: cannot access 'missing.txt': No such file or directory notes.txt
ls reports the missing name on standard error, still lists the ones that exist, and exits
with status 2 ("serious trouble"); 1 is reserved for minor problems such as an unreadable
subdirectory.
Common pitfalls
Parsing
lsoutput (for f in $(ls)) breaks on spaces and newlines in names;file with spaces.txtwould become three words. Use a glob:for f in *; do ...; done.ls -lon a symlink to a directory shows the link;ls -l link/(trailing slash) orls -lL linkshows what is inside or what it points at.ls *with many files can fail with "Argument list too long": the shell expands the glob beforelsruns. Uselson the directory, orfind.Sizes in
ls -lare apparent sizes. A sparse file can look huge while using almost no disk;dushows actual usage.
GNU vs BSD and macOS
The examples above ran the GNU/Linux version. These are the differences that matter on macOS and the BSDs (written from their documentation, not run here).
Color: GNU uses
ls --color=auto(often set as an alias); BSD/macOS usesls -Gor theCLICOLORvariable.--time-style,--group-directories-firstand other long options are GNU-only. macOSls -lTshows the full time.macOS
ls -lmay show an@or+after the permissions for extended attributes or ACLs; GNU shows+for ACLs and.for an SELinux context.
Options worth knowing
A selection, described in our own words. For the full list, see ls(1) or ls --help.
| Option | What it does |
|---|---|
-l | Long format: permissions, links, owner, group, size, time, name. |
-a, --all | Include names starting with a dot, plus . and .. |
-A, --almost-all | Include dotfiles but not . and .. |
-h, --human-readable | With -l, print sizes like 4.0K and 12M. |
-t | Sort by modification time, newest first. |
-S | Sort by file size, largest first. |
-r, --reverse | Reverse the sort order. |
-R, --recursive | List subdirectories recursively. |
-d, --directory | List directories themselves, not their contents. |
-F, --classify | Append / to directories, * to executables, @ to symlinks. |
-1 | One name per line. |
-i, --inode | Print each file's inode number. |
-n, --numeric-uid-gid | Like -l, but show numeric user and group IDs. |
-L, --dereference | Show the target of symlinks instead of the links. |
-X | Sort alphabetically by extension. |
-v | Natural sort of numbers within names (file2 before file10). |
-u | With -l, show access time; with -t, sort by it. |
-c | With -l, show status-change time; with -t, sort by it. |
--color[=WHEN] | Colorize names: never, always or auto. |
--time-style STYLE | Date format: full-iso, long-iso, iso, locale or +FORMAT. |
--group-directories-first | List directories before files. |
--sort WORD | Sort by none, size, time, version, extension or width. |
Related
treeDraw a directory and everything below it as an indented tree.statShow a file's metadata (size, permissions, owner, inode, timestamps), or print chosen fields in your own format.findWalk a directory tree and print, or act on, every file that matches a set of tests.duShow how much disk space files and directories use, totalled per directory.fileGuess what kind of data a file holds by looking at its contents, not its name.- GlobbingHow *, ? and [...] match file names, what happens when nothing matches, and the shell options nullglob, failglob, dotglob, globstar and extglob.
- Word splittingWhen bash splits expansion results into separate words, how IFS controls it, and why `for line in $(cat file)` does not loop over lines.