Table of contents
Open Table of contents
Intro
I plan to make blog posts about GNU Core Utilities in more detail. ls
will be the first one. Everyone knows how to use ls
, but most are not familiar even with 90% of this command. So, this series will show basic commands’ lesser-known but useful functionalities.
ls
is a command to list directory content. It was written by Richard M. Stallman and David MacKenzie.
Processed Documentation
Basic
You can skip if you’re already familiar with how ls
works at the surface.
First of all:
- Hidden files are those that start with a dot (
.
) - Syntax:
ls [OPTIONS]... [FILES]...
- Options can be multiple and combined like
-la
- If no file or directory is provided, it defaults to the current directory
- Options can be multiple and combined like
- Exit codes:
0
: success1
: minor issue (e.g., permission denied)2
: serious problem (e.g., file doesn’t exist)
Most commonly used options
# Get help
ls --help
man ls
# List current directory
ls
another_folder some_file some_folder
# List all files including hidden ones
ls -a
. .. another_folder .seemefile some_file some_folder
# Long listing format with permissions, size, timestamp, etc.
ls -l
total 12
drwxr-xr-x 2 elnur users 4096 Jul 16 22:42 another_folder
-rw-r--r-- 1 elnur users 13 Jul 16 22:41 some_file
drwxr-xr-x 2 elnur users 4096 Jul 16 22:41 some_folder
# Combine options
ls -la
total 20
drwxr-xr-x 4 elnur users 4096 Jul 16 22:47 .
drwx------ 36 elnur users 4096 Jul 16 22:43 ..
drwxr-xr-x 2 elnur users 4096 Jul 16 22:42 another_folder
-rw-r--r-- 1 elnur users 0 Jul 16 22:47 .seemefile
-rw-r--r-- 1 elnur users 13 Jul 16 22:41 some_file
drwxr-xr-x 2 elnur users 4096 Jul 16 22:41 some_folder
Understanding the Long Listing Format (ls -l
)
The long format reveals much more than just filenames. Here’s a breakdown of what this line means:
-rw-r----- 1 elnur users 13 Jul 16 22:41 some_file
Field | Description |
---|---|
-rw-r--r-- | File permissions and type: First character: file type ( - = regular file) Next 9 characters: permission bits in 3 groups: - First 3: Owner (user who owns the file) - Second 3: Group (users in the file’s group) - Last 3: Others (everyone else) |
1 | Hard link count — Number of hard links to the file |
elnur | Owner — The user who owns the file |
users | Group — The group that owns the file |
13 | File size in bytes |
Jul 16 22:41 | Last modification date and time |
some_file | Filename — The name of the file |
If the file were a directory, symbolic link, or special file, the first character would change:
Symbol | Type | Notes |
---|---|---|
- | Regular file | Standard file |
d | Directory | Contains other files/directories |
l | Symbolic link | Points to another file |
In next 9 charachters, each group represents permissions:
Symbol | Meaning | Applies to |
---|---|---|
r | read | Can read/view file contents |
w | write | Can modify the file (or add/remove inside a directory) |
x | execute | Can run the file if it’s a program or script (or cd into a directory) |
- | no permission | That specific permission is not granted |
Additional Information about -l
will be covered later.
Intermediate
These options aren’t often used by beginners, but they’re useful in real-world scripting or file inspection tasks:
Show All Files (but skip .
and ..
)
ls -a
shows hidden files and .
, ..
entries, which can clutter the output. So, you can do the following:
ls -A
another_folder .seemefile some_file some_folder
Ignore Backup Files
Some editors create temporary or backup files ending in ~
ls
config.txt config.txt~ notes.txt
ls -B # or ls --ignore='*~'
config.txt notes.txt
Great for directories filled with auto-saved files you don’t care about.
Only List Directories Themselves
By default, if you give ls
a directory name, it lists its contents. But if you want to output only directories:
ls -d */
another_folder/ some_folder/
# Or for a specific directory:
ls -d my_folder
my_folder
Ignore Specific File Patterns
Use --ignore
or -I
:
# ignore specific pattern
ls --ignore='*.log'
# or patterns
ls --ignore='*.log' --ignore='*.tmp'
# ---
ls
app.log app.tmp app.py
ls --ignore='*.log' --ignore='*.tmp'
app.py
Follow Symbolic Links (or Not)
By default, ls
lists the symlink itself, not the file it points to. If you want details about the target file, you use -L
or --dereference
:
ls -lL my_link
This shows permissions, owner, and size of the destination file.
ls -l latest_report
lrwxrwxrwx 1 elnur users 10 Avq 2 16:38 latest_report -> report.txt
ls -lL latest_report
-rw-r--r-- 1 elnur users 17 Avq 2 16:38 latest_report
Useful for checking if a symlink is valid or broken.
ls -l broken_link
lrwxrwxrwx 1 elnur users 11 Avq 2 16:39 broken_link -> missing.txt
ls -lL broken_link
ls: cannot access 'broken_link': No such file or directory
Recursively List Everything
To see all files in all subdirectories, use -R
or --recursive
:
ls
another_folder some_file some_folder some.json
ls -R
.:
another_folder some_file some_folder some.json
./another_folder:
another_file.txt baby_folder
./another_folder/baby_folder:
./some_folder:
Human-Readable File Sizes
By default, file sizes in ls -l
are shown in bytes, which is hard to scan for large files.
ls -lh
total 16K
drwxr-xr-x 3 elnur users 4,0K Avq 2 16:45 another_folder
-rw-r--r-- 1 elnur users 13 İyl 16 22:41 some_file
drwxr-xr-x 2 elnur users 4,0K İyl 16 22:41 some_folder
-rw-r--r-- 1 elnur users 3,8K İyl 28 13:02 some.json
# K = kilobytes (1024 bytes)
# M = megabytes
# G = gigabytes
If you prefer SI units (powers of 1000, not 1024):
ls --si -l
total 17k
drwxr-xr-x 3 elnur users 4,1k Avq 2 16:45 another_folder
-rw-r--r-- 1 elnur users 13 İyl 16 22:41 some_file
drwxr-xr-x 2 elnur users 4,1k İyl 16 22:41 some_folder
-rw-r--r-- 1 elnur users 3,9k İyl 28 13:02 some.json
More File Types with -l
In the basic section, you saw common file types (-
= file, d
= directory, l
= symlink). But ls -l
can show other types too:
Symbol | Type | Notes |
---|---|---|
b | Block device | Hardware devices that read/write data in blocks (e.g., disks) |
c | Character device | Hardware devices with stream-based access (e.g., keyboard, terminal) |
p | Named pipe (FIFO) | Special file for inter-process communication |
s | Socket | Network or inter-process communication endpoint |
D | Door (Solaris) | Special file type for fast RPC calls on Solaris systems |
M | Migrated file | File stored offline (e.g., on tape or remote storage) |
ls -l /dev
brw-rw---- 1 root disk 8, 0 Jul 16 22:41 sda
crw-rw-rw- 1 root root 1, 3 Jul 16 22:41 null
mkfifo pipppip
ls -l
prw-r--r-- 1 elnur users 0 Avq 2 17:09 pipppip
Advanced
Show Full Timestamps
By default, ls -l
shows only month, day, and time (or year, if not modified recently).
--full-time
reveals the complete timestamp with seconds and timezone:
ls --full-time
-rw-r--r-- 1 elnur users 13 2024-07-16 22:41:02.000000000 +0400 some_file
Group-Only or Owner-Only Listing
When you want to hide some columns in ls -l
:
-g
→ like-l
but omit owner column.-o
→ like-l
but omit group column.-G
→ or--no-group
- omit group column and use-l
with it
ls -l
-rw-r--r-- 1 elnur users 3887 İyl 28 13:02 some.json
ls -g
-rw-r--r-- 1 users 3887 İyl 28 13:02 some.json
ls -o
-rw-r--r-- 1 elnur 3887 İyl 28 13:02 some.json
ls -lG
-rw-r--r-- 1 elnur 3887 İyl 28 13:02 some.json
Show Inode Numbers
Every file has an inode in the filesystem.
ls -i
14156904 another_folder 11373995 some_file 14156903 some_folder 11374841 some.json
What is the application of such knowledge? - you would ask.
Find Hard Links
Knowing the inode of one file you can find its hard link in another place:
ls -i some.json
11374841 some.json
find . -inum 11374841
./remote_folder/another.json
./some.json
Delete Files with Annoying and Strange Names
Sometimes files have names that are hard to type (e.g., starting with -
, containing spaces, or control characters). You can delete them by inode:
ls -i
14156904 another_folder 14171799 remote_folder 11373995 some_file 14156903 some_folder 11374841 some.json 11297265 у_тебя_нет_кириллицы_ха-ха-ха
find . -inum 11297265 -exec rm {} \;
ls
another_folder remote_folder some_file some_folder some.json
(Yes, some shells allow you to Tab-Tab or copy and paste, but some not)
Show Disk Usage in Blocks
With -s
or --size
, ls
shows how many filesystem blocks a file occupies:
ls -s
total 20
4 another_folder 4 remote_folder 4 some_file 4 some_folder 4 some.json
ls -sh
total 20K
4,0K another_folder 4,0K remote_folder 4,0K some_file 4,0K some_folder 4,0K some.json
Units depend on the filesystem’s block size. Use -h
with -s
for human-readable sizes.
Show SELinux Context
If your system uses SELinux, -Z
displays the security context:
ls -Z
-rw-r--r--. elnur users unconfined_u:object_r:user_home_t:s0 some_file
Handy for debugging permission issues in SELinux-enabled environments.
Sorting the Output
ls
can sort by different criteria:
Option | Sort by | Notes |
---|---|---|
-t | modification time (newest) | Combine with -r for oldest first |
-u | last access time | Works with -t or --sort=time |
-S | file size (largest first) | Combine with -r for smallest first |
-X | extension | Groups files by suffix |
ls
another_folder remote_folder some_file some_folder some.json
ls -t
remote_folder another_folder some.json some_file some_folder
ls -tr
some_folder some_file some.json another_folder remote_folder
ls
a.json another_folder a.txt b.txt remote_folder some_file some_folder some.json
ls -X
another_folder remote_folder some_file some_folder a.json some.json a.txt b.txt
You can also use the explicit --sort=WORD
form:
--sort=size
--sort=extension
--sort=none
To disable sorting (faster for huge directories), use:
ls -f
# This also forces -a
General Output Formatting
-1
→ one entry per line (good for scripts)-C
→ multi-column output (default when output is a terminal)-m
→ comma-separated list--format=long
/--format=commas
→ same effect as above, but explicit.
You can also set environment variable LS_COLORS
to control colors, or use --color=auto/always/never
.
ls -1
another_folder
some_file
some_folder
some.json
ls -m
another_folder, some_file, some_folder, some.json
Formatting File Timestamps
Use --time=WORD
to choose which time to display:
--time=atime
/access
--time=ctime
/status
--time=mtime
/modify
(default)
Combine with --time-style=STYLE
to format the date:
ls --time-style=full-iso -l
total 16
drwxr-xr-x 3 elnur users 4096 2025-08-02 16:45:56.733863608 +0400 another_folder
-rw-r--r-- 1 elnur users 13 2025-07-16 22:41:41.544841538 +0400 some_file
drwxr-xr-x 2 elnur users 4096 2025-07-16 22:41:31.601828588 +0400 some_folder
-rw-r--r-- 1 elnur users 3887 2025-07-28 13:02:35.809927528 +0400 some.json
ls --time-style=+%Y/%m/%d_%H:%M -l
total 16
drwxr-xr-x 3 elnur users 4096 2025/08/02_16:45 another_folder
-rw-r--r-- 1 elnur users 13 2025/07/16_22:41 some_file
drwxr-xr-x 2 elnur users 4096 2025/07/16_22:41 some_folder
-rw-r--r-- 1 elnur users 3887 2025/07/28_13:02 some.json
Formatting the File Names
- Quoting:
--quote-name
wraps names in double quotes. - Literal:
-N
or--literal
shows raw names without escaping. - Escape:
-b
shows non-printable characters as C-style escapes. - Octal:
-q
hides unprintables as?
. - Custom quoting:
2WORD
(literal
,shell
,c
,escape
, etc.).
ls --quote-name
"another_folder" "some_file" "some_folder" "some.json" "my file with spaces"
Practical Scenarios
You can find some use cases:
- Quickly Spot Large Files in a Directory Tree
ls -lhSR
- List Files Accessed in the Last 24 Hours
ls -ltu --time-style=full-iso | head
- Compare Two Files by Inode to Detect Hard Links
ls -i file1 file2
- Check Symlink Targets and Their Validity
ls -l
lrwxrwxrwx 1 elnur users 10 Aug 2 16:38 latest -> report.txt
ls -lL latest
-rw-r--r-- 1 elnur users 17 Aug 2 16:38 latest
If -lL
fails with “No such file or directory,” the symlink is broken — time to fix it.
- See SELinux Context When Permissions Look Fine
ls -lZ
If a file has correct rw
permissions but is still inaccessible, the SELinux label may be the reason. Compare with a working file to spot mismatches.
- Generate a Script-Friendly List of Files
ls -1Q --quoting-style=shell > files.txt
Perfect for feeding file lists into other scripts without worrying about spaces in names.
- List Only Directories, Sorted by Size
ls -lhd */ | sort -h -k5
Additional
eza
: A Modern Replacement for ls
While ls
is everywhere and dependable, many Linux users now prefer eza
— a modern, colorful, and more feature-rich drop-in replacement.
eza
keeps the core behavior of ls
but adds:
- Git integration — shows file Git status right in the listing
- Icons (with a nerd font)
- Better colors and formatting
- Tree view with
--tree
- More human-friendly output by default
Honorable Mentions
lsd
tree
colorls
Conclusion
ls
might be one of the first commands you ever learn on Linux, but it’s far from basic.
Whether you’re a casual user cleaning up a folder, a sysadmin diagnosing a permissions issue, or a developer automating a deployment script, knowing these hidden gems will save you time and headaches.