File types in Linux
Seven file types in Linux and the first character in ls -l.
Why this matters
On a server you constantly open, copy, and archive files. But a file name
doesn't always have data behind it. It could be a directory, a link, a
disk, a socket, or a pipe between processes. A script that doesn't
distinguish these types crashes with an error or silently hangs. The
object's type shows up as a single letter in ls -l, and in this lesson
you'll learn to read it.
The seven file types
Linux has seven kinds of files, not one. A regular file with text, a directory, a link, a disk, a terminal. All of these are different types, and the system tells them apart. A file type is a category of object in the filesystem. It tells the kernel how to work with the object.
The type shows up as the first character in the output of ls -l. It's
one letter before the permissions:
ls -l
-rw-r--r-- 1 deploy deploy 1240 Jul 11 10:14 notes.txt
drwxr-xr-x 2 deploy deploy 4096 Jul 11 10:12 projects
lrwxrwxrwx 1 deploy deploy 9 Jul 11 10:15 current -> notes.txt
notes.txt has a hyphen first: that's a regular file. projects has a
d, a directory. current has an l, a symbolic link. Its size, 9,
is the length of the path notes.txt that it stores.
Full list of first characters:
| Character | Type | Example |
|---|---|---|
- | regular file | text, image, binary |
d | directory | /home, /etc |
l | symbolic link | current -> notes.txt |
b | block device | disk /dev/sda |
c | character device | terminal /dev/tty |
s | socket | /run/docker.sock |
p | named pipe, FIFO | pipe between processes |
The extension in a name says nothing about the type. photos.zip could
turn out to be a directory or a pipe: a person picks the name, the kernel
assigns the type when the object is created.
Devices, sockets, and pipes
Block and character devices live in /dev. Look there with ls -l, and
you'll see b and c in the first column:
ls -l /dev/sda /dev/tty
brw-rw---- 1 root disk 8, 0 Jul 11 09:00 /dev/sda
crw-rw-rw- 1 root tty 5, 0 Jul 11 09:00 /dev/tty
Instead of a size, devices show two numbers separated by a comma: the major and minor number. The kernel uses them to find the driver and the specific device.
A block device (b) is read and written in blocks, and any block can be
addressed directly. That's how disks work. A character device (c)
sends and receives data as a stream of bytes. That's how terminals,
serial ports, and /dev/null work.
A socket (s) and a named pipe, or FIFO (p), exist for communication
between processes. Through the socket /run/docker.sock, the docker
client talks to the daemon:
ls -l /run/docker.sock
srw-rw---- 1 root docker 0 Jul 11 09:00 /run/docker.sock
A FIFO is created with mkfifo. It passes data from one process to
another, like a pipe with a name in the filesystem:
mkfifo pipe
ls -l pipe
prw-r--r-- 1 deploy deploy 0 Jul 11 10:20 pipe
A pipe keeps no data of its own on disk, so its size is always 0. Reading
from a FIFO waits until someone on the other side starts writing. That's
why cat pipe with no writer process just hangs until you press Ctrl+C.
What "everything is a file" means
The phrase "everything is a file" in Linux describes a single interface.
A disk, a terminal, a socket, and a plain text file are all opened, read,
and written with the same system calls: open, read, write,
close. A program doesn't need to know what kind of object it is: it
works with it through file operations.
Underneath, though, the objects are built differently. Writing a byte to
/dev/sda and writing a byte to notes.txt have different
consequences, even though it's the same write call.
Every object a process opens gets a file descriptor inside that process.
A file descriptor is a non-negative integer, unique to the process, that
refers to an open file. Three numbers are taken from the moment a
process starts: 0 is stdin (input), 1 stdout (output), 2 stderr
(errors). A process's open descriptors are visible in /proc/<PID>/fd,
and /proc/self always points to the process reading that directory:
ls -l /proc/self/fd
lrwx------ 1 deploy deploy 64 Jul 11 10:21 0 -> /dev/pts/0
lrwx------ 1 deploy deploy 64 Jul 11 10:21 1 -> /dev/pts/0
lrwx------ 1 deploy deploy 64 Jul 11 10:21 2 -> /dev/pts/0
...
All three of ls's descriptors point to the terminal /dev/pts/0, a
character device. There can be more lines: those are files the process
opened itself.
A directory is a file too
A directory is a special type of file that stores a list of names and inode numbers. An inode is a structure holding a file's metadata (permissions, owner, size, the addresses of its data blocks), and it has a number. A name in a directory just points to an inode number:
ls -li notes.txt
1310721 -rw-r--r-- 1 deploy deploy 1240 Jul 11 10:14 notes.txt
The number 1310721 on the left is the inode number. The directory
stores the pair "name → 1310721"; the data itself lives elsewhere.
/proc and /sys don't live on disk. They're virtual filesystems: the
kernel assembles their contents in memory at the moment you read them.
The file /proc/cpuinfo takes up no space on disk:
df /proc
Filesystem 1K-blocks Used Available Use% Mounted on
proc 0 0 0 - /proc
A size of 0 isn't a mistake: a virtual filesystem has no blocks on a
storage device.
How to tell the type besides ls -l
Besides the first letter of ls -l, two other ways show the type.
stat -c '%F %n' prints the type in words:
stat -c '%F %n' notes.txt projects pipe
regular file notes.txt
directory projects
fifo pipe
For an empty regular file, stat prints regular empty file.
In scripts, you check the type with the test command (also written as
[ ... ]):
| Test | True when |
|---|---|
[ -f x ] | x is a regular file |
[ -d x ] | x is a directory |
[ -L x ] | x is a symbolic link |
[ -p x ] | x is a FIFO |
[ -S x ] | x is a socket |
[ -b x ] | x is a block device |
[ -c x ] | x is a character device |
Every test except -L follows a link to its target. If x is a link to
a regular file, [ -f x ] is also true.
Common mistakes
Judging the type by name. events.log could be a pipe, data.tar a
directory. Look at the first letter of ls -l, not the extension.
Not checking the type in scripts. A script that expects regular
files will get an Is a directory error on a directory, and behave very
differently on a pipe or a device. If you need only regular files, check
the type before reading.
Treating a directory as something separate from files. A directory is a file too, with its own inode and permissions. Permissions on a directory decide whether you can list the names in it and create files inside it.
Taking "everything is a file" literally. The same interface doesn't mean the same behavior. Writing to a block device changes the disk, bypassing the filesystem.
What you'll need for the tasks
- The first letter in
ls -l:-regular,ddirectory,llink,bblock device,ccharacter device,ssocket,pFIFO. stat -c '%F %n' <file>: the type in words.- The tests
[ -f x ],[ -d x ],[ -p x ], and others from the table above. - Reading a FIFO with no writer process hangs. You can exit with Ctrl+C.
In the next lesson we'll cover inodes and hard links: how a name is connected to data, and why a file can live under several names.
Interview-style questions
Interview questions on this topic: 1
Included with practice access, $49 one-time. Get access
Answer out loud or in writing, then reveal the answer and honestly mark whether you knew it.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 2 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson linux-fs-deep-01-file-types
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress