
Breakdown8 min read
Decoding “Everything is a file”
Linux · Unix · Systems
Unix’s most quoted slogan is a design contract, not poetry: many resources share open, read, write, and close. What “file” means, where the metaphor is honest, and where it bends.
“Everything is a file” shows up on stickers and one-line answers that end the conversation without explaining anything. This note is a breakdown of the slogan: what the contract actually is, what “file” means, the everyday surfaces it shows up on, and where the metaphor bends.
One-sentence crystal
Everything is a file means many kernel-managed resources share a uniform path + file-descriptor interface (open, read, write, close, and kin)—not that every object is a disk blob or that
catis always the right tool.
The slogan is a uniform interface
Taken literally, the phrase is false: a serial port is not a PDF. Taken as system design, it is sharp:
Many different kernel-managed resources are exposed through the same family of operations—open (or openat), read, write, close, and often ioctl / mmap / poll—so programs and shell tools do not need a new API for every kind of object.
| Casual reading | Useful reading |
|---|---|
| “Disk bytes and devices are the same thing” | “I can name many resources with paths and talk to them with a small set of syscalls” |
“cat works on everything forever” | “cat works on anything that behaves like a byte stream under the VFS” |
| “No special cases” | “Special cases still exist; the file model is the default door” |
A more accurate form of the phrase is sometimes “everything is a file descriptor” or “everything is a stream of bytes”: the resource need not have a filename or live on a physical disk—it only needs to behave like a stream you can read from or write to.
So “everything is a file” really means: everything that can usefully wear a file descriptor often does.
What “file” means here
In this context, a file is not “something in a folder on disk.” It is closer to:
- An object the kernel tracks (identity + operations).
- Often a path in a hierarchical namespace (
/, mount points). - After
open, a file descriptor (a small integer in your process) that stands for “this open instance with these flags and this offset/mode.”
path ──open()──► file descriptor (fd)
│
├── read / write / lseek
├── ioctl / mmap / fcntl
└── close
Paths are for finding. FDs are for using.
| Layer | Example | What you hold |
|---|---|---|
| Path name | /etc/hosts, /dev/ttyUSB0, /proc/self/status | String the VFS resolves |
| Open file | Result of open(...) | FD 3, 4, … in this process |
| Underlying object | Regular file, socket, pipe, device, pseudo-file | Kernel object with ops |
Two processes can open the “same” path and get different FDs with different offsets and flags.
Four places the idea shows up every day
1. Regular files and directories
Disk-backed bytes and directory entries—what non-Unix people usually mean by “file.” Unix adds:
- Everything is bytes at the read/write level.
- Metadata (mode, owner, timestamps, size) lives beside content.
- Directories are special: list, link, unlink—not free-form byte stores you should rewrite with
echo.
2. Devices under /dev
Character and block devices are nodes that look like files:
ls -l /dev/null /dev/zero /dev/urandom /dev/tty 2>/dev/null
| Node (typical) | “File” behavior | Reality |
|---|---|---|
/dev/null | Write discards; read returns EOF | Kernel black hole |
/dev/zero | Read returns zeros | Infinite zero source |
/dev/urandom | Read returns entropy bytes | Randomness as a stream |
/dev/ttyUSB0 | read/write serial traffic | Hardware as a stream |
| Block device | Often raw sector I/O | Disk/partition—dangerous if misused |
# Same tools; different objects
echo hello > /tmp/note.txt
echo hello > /dev/null # disappears on purpose
head -c 16 /dev/urandom | xxd # binary “file” that is not on disk
3. Process and kernel state under /proc and /sys
Pseudo-filesystems: no real disk file behind most paths. Read to observe; sometimes write to configure.
cat /proc/self/status | head
cat /proc/meminfo | head
ls /sys/class/net
cat /sys/class/net/lo/operstate
| Tree | Mental model |
|---|---|
/proc | Process table + some system snapshots as files |
/sys | Devices, drivers, and attributes as a navigable tree |
/proc/sys | Tunables as files |
The namespace is the API surface for a large class of operations: shell tools work without a separate “system API client” for every knob.
4. Pipes, sockets, and stdio
Pipes and many sockets are also FD-backed. The shell’s | is the slogan’s social form:
dmesg | grep -i error | tail -n 20
Each stage reads a stream and writes a stream. Filters compose because the interface is the same shape.
Standard streams are FDs by convention:
| FD | Name | Usual meaning |
|---|---|---|
| 0 | stdin | Input stream |
| 1 | stdout | Normal output |
| 2 | stderr | Diagnostics |
Redirection (>, 2>, 2>&1) rearranges which objects those FDs point at—a file, /dev/null, or a pipe.
Decoding table: what you can do
| You see… | Treat it as… | Typical ops | Trap |
|---|---|---|---|
Path under home or /etc | Persistent bytes + metadata | cat, editors, stat | Permissions, atomic replace |
/dev/tty* and friends | Byte stream to hardware | serial tools, open in a program | Baud, exclusive open, access rights |
/proc/<pid>/… | Live process snapshot | cat, ls | Values change under you |
/sys/... | Driver/hardware attribute | cat / careful write | Wrong write can break devices |
| Pipe / FIFO | Stream between processes | shell pipe, mkfifo | Buffering, SIGPIPE |
| Socket | Connection endpoint | network tools, app protocols | Not always a path you can ls |
| Directory | Namespace node | ls, find, mkdir | Not a flat byte file |
Many resources share path + file-descriptor I/O so the same tools and syscalls apply; that does not mean every object is a disk blob or that cat is always the right tool.
What the slogan depends on
Virtual File System (VFS). Different backends (disk filesystems, tmpfs, procfs, sysfs, device nodes) plug into one layer so open/read look similar upstairs. Uniformity is an abstraction.
Permissions. A path that looks like a file still has mode bits, ownership, and sometimes stricter policy. “I can see the path” ≠ “I may open it.”
Text when it is text. Many /proc and /sys attributes are human-readable on purpose.
Where the slogan bends
| Pressure | What happens |
|---|---|
| Sockets and some IPC | Often FDs, but not always a comfortable path name; protocols live above raw bytes |
ioctl and private APIs | Device-specific commands that are not portable read/write semantics |
| Complex devices | May expose a node yet need specialized APIs beyond a byte stream |
| Non-seekable “files” | Pipes and many devices: no meaningful lseek; size may be zero or meaningless |
| Containers / namespaces | The same path string can mean different objects in different views |
Mature form of the slogan:
Prefer a file-like interface when it reduces special cases—without pretending special cases vanished.
A five-minute drill
On any Linux box you are allowed to use:
# 1) Same command family, three object kinds
wc -c /etc/hosts
wc -c /proc/version
# wc -c /dev/zero # infinite “file” — Ctrl-C when you have the idea
# 2) FDs for this shell
ls -l /proc/self/fd
# 3) Where does this path really live?
readlink -f /etc/hosts
findmnt -T /etc
If those steps feel obvious, you already use the slogan.
Prefer precise language
| Avoid saying | Prefer saying |
|---|---|
| “Everything is a file, so Linux is simple” | “Linux exposes many resources through a uniform file/FD interface” |
| “Just cat the device” | “If it is a byte stream device node, stream tools may work; check mode and docs” |
| “/proc is the filesystem” | “/proc is a pseudo-filesystem projecting kernel state as files” |
| “Namespaces break the model” | “Namespaces rebind the view; the model still holds inside that view” |
Guess what?
The commands you type every day are files too, not a separate magic class of object.
When you run ls, grep, or a helper script, the shell resolves a path (via PATH), opens that object, and executes it. Same namespace; same “look it up, then use it” story.
type ls
# often: ls is /bin/ls (or hashed, or a shell builtin—type will say)
command -v grep
which python3 # if which is installed
If the resolved path is a script (starts with #!), you can often just read it:
# Example shape—paths differ by distro
file "$(command -v update-alternatives)" # may say "shell script"
# cat "$(command -v some-helper-script)" # open the text; edit with care
If it is a compiled binary, cat will dump noise (or mess your terminal). Prefer:
file "$(command -v ls)" # ELF executable, …
less "$(command -v ls)" # optional; binary, not a novel
Builtins (cd, type itself in bash) are the honest exception: they live inside the shell, so there is no separate path to cat. Everything else you “just run” is still, under the slogan, a named object in the filesystem—or a stream once it is open.
The surprise is not that programs are special. The surprise is that they never left the model: find the path, treat it as a file, and only then worry about “is this text I can read, or a binary I should not.”
Closing
Decode the phrase as interface uniformity + hierarchical naming + file descriptors, not as ontology. Use it to navigate regular files, /dev, /proc, /sys, pipes, and stdio without fear. Drop it when the problem needs a real protocol, a real ioctl, or a specialized API.
If you can open the right path, read the right stream, and know whether you are holding a disk object, a device stream, or a synthetic control surface, you are practicing what the slogan was for.
Was this page helpful?