Sign in
← Linux: file system in depth
10 min · easy · Lesson 1 of 7

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:

CharacterTypeExample
-regular filetext, image, binary
ddirectory/home, /etc
lsymbolic linkcurrent -> notes.txt
bblock devicedisk /dev/sda
ccharacter deviceterminal /dev/tty
ssocket/run/docker.sock
pnamed pipe, FIFOpipe 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 [ ... ]):

TestTrue 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

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.

Done reading?

Mark the lesson as read to move your track progress forward.

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.

Get access for $49

Tell the agent: use MCP izi, give me lesson linux-fs-deep-01-file-types