Navigating the File System
Finding your way around a Linux system from the command line: the directory tree, absolute and relative paths, and the commands pwd, cd and ls.
One directory tree
A Linux system has a single directory tree. It starts at the
root directory, written as /, and
every file, every disk and every Universal Serial Bus (USB) stick is
somewhere below it. The same character separates the levels of a
path: /home/ana/projects is the directory
projects, inside ana, inside
home, inside /.
The layout of that tree follows the Filesystem Hierarchy Standard (FHS), which says which directory holds what, so that you find the same places on Ubuntu, Debian, Fedora and Red Hat Enterprise Linux (RHEL). The table of the main directories is in Linux at a Glance. For moving around, these are the ones you visit most:
| Directory | Why you go there |
|---|---|
/home/user
|
Your own files and personal settings |
/etc |
To read or change the configuration of a service |
/var/log |
To read log files when something goes wrong |
/tmp |
Scratch space; anyone can write there, and it may be emptied at every restart |
/usr/bin, /usr/sbin |
Where the programs are; sbin holds tools for
administrators
|
/opt |
Add-on software from vendors, each in its own directory |
/mnt, /media |
Where extra disks and removable media appear |
/root |
The home directory of the root user (closed to ordinary users) |
On current distributions /bin, /sbin and
/lib are only links to their counterparts in
/usr (the "merged /usr"), so
/bin/ls and /usr/bin/ls are the same file.
Older documentation that distinguishes them still works.
/. The highlighted directory has one
absolute path, and a different relative path from every other
place in the tree.
Where am I? The working directory
Every shell (and every program) has a
working directory, also
called the current directory: the directory it is "in", where names
without a path are looked up and new files are created by default.
pwd (print working directory) shows it, and the default
prompt shows it too.
$ pwd
/home/ana
When a command says "No such file or directory" for a file you are sure exists, the first thing to check is the working directory.
The home directory and ~
Your home directory is the
directory that belongs to your account: it holds your files and your
personal settings, and a new shell starts there. For ordinary users
it is /home/name; for root it is /root. The shell offers three ways to
write it:
-
~(tilde) on its own or at the start of a path:~/projectsis/home/ana/projectsfor Ana. -
~ben: the home directory of userben. -
$HOME: the variable that holds the path, the form to use in scripts.
The shell replaces ~ before the command runs, and only
where it is not quoted: "~/projects" in quotes means a
directory literally named ~ inside the working
directory. Use "$HOME/projects" when you need quotes.
Absolute and relative paths
A path names a file or directory, and there are two kinds:
-
An absolute path starts with
/and describes the way from the root:/var/log/syslog. It means the same thing no matter where you are, which makes it the right choice in scripts, configuration files and instructions for colleagues. -
A relative path does not
start with
/and is read from the working directory: if you are in/var,log/syslogmeans/var/log/syslog. It is shorter to type, but its meaning changes when you move.
Two special names exist in every directory: . is the
directory itself and .. is its parent (one level up).
../../home goes two levels up, then into
home. In /, .. is simply
/ again.
The examples on this page start in an empty folder that serves as
the home directory. They build a small tree first, and use
sed to show the home directory as ~ in the
output of pwd, the way the prompt does.
mkdir -p projects/website/css projects/notes
cd projects/website/css # relative: from where we are
pwd | sed "s|$HOME|~|"
cd ../../notes # up two levels, then down into notes
pwd | sed "s|$HOME|~|"
cd ~/projects/website # from the home directory
pwd | sed "s|$HOME|~|"
cd ./css/../../notes # . and .. can appear anywhere
pwd | sed "s|$HOME|~|"
~/projects/website/css ~/projects/notes ~/projects/website ~/projects/notes
mkdir -p creates a directory together with any missing
parent directories (see
Working with Files and Directories).
Moving around with cd
| Command | Goes to |
|---|---|
cd /var/log |
An absolute path |
cd log |
A relative path, below the working directory |
cd .. |
One level up |
cd or cd ~ |
Your home directory |
cd - |
The previous directory (and prints its name), handy for switching back and forth |
mkdir -p projects/website/css projects/notes
cd projects/website
pwd | sed "s|$HOME|~|"
cd css
cd ..
pwd | sed "s|$HOME|~|"
cd ../notes
pwd | sed "s|$HOME|~|"
cd "$OLDPWD" # back to where we were
pwd | sed "s|$HOME|~|"
cd # home
pwd | sed "s|$HOME|~|"
~/projects/website ~/projects/website ~/projects/notes ~/projects/website ~
The variable OLDPWD holds the previous directory, and
cd "$OLDPWD" is what cd - does. At the
prompt, cd - is quicker to type, and it also prints the
directory it returns to:
$ cd /etc/ssh
$ cd /var/log
$ cd -
/etc/ssh
Listing with ls
ls lists the contents of the working directory, or of
the directories and files you name. In a terminal it prints the
names in columns; when its output goes into a file or another
command, it prints one name per line. ls -1 (the digit
one) asks for one per line explicitly, which is what the examples
here use.
| Option | Meaning | Example |
|---|---|---|
-l |
Long format: type, permissions, owner, size, date | ls -l /etc/ssh |
-a |
All, including hidden names and . and
..
|
ls -a ~ |
-A |
Almost all: hidden names, without . and
..
|
ls -A ~ |
-h |
Human-readable sizes (K, M, G), together with
-l
|
ls -lh /var/log |
-t |
Sort by modification time, newest first | ls -lt /var/log |
-S |
Sort by size, largest first | ls -lS ~/Downloads |
-r |
Reverse the sort order | ls -ltr (newest last) |
-R |
Recursive: also list every subdirectory | ls -R projects |
-1 |
One name per line | ls -1 |
-d |
Show a directory itself, not its contents | ls -ld /tmp |
Sorting in action. The example creates three log files of different
sizes and gives each a fixed modification time with
touch -d:
mkdir logs && cd logs
head -c 300 /dev/zero > app.log
head -c 5000 /dev/zero > access.log
head -c 40 /dev/zero > error.log
touch -d '2026-09-01 08:00' access.log
touch -d '2026-09-15 08:00' app.log
touch -d '2026-09-30 08:00' error.log
echo "By name:"; ls -1
echo "By time:"; ls -1t
echo "By size:"; ls -1S
echo "Smallest first:"; ls -1Sr
By name: access.log app.log error.log By time: error.log app.log access.log By size: access.log app.log error.log Smallest first: error.log app.log access.log
(head -c 300 /dev/zero simply produces 300 bytes to
fill the file; && runs cd only if
mkdir succeeded.) With -R,
ls walks the whole tree below a directory:
mkdir -p site/css site/images
touch site/index.html site/css/style.css site/images/logo.png
ls -R site
site: css images index.html site/css: style.css site/images: logo.png
The long listing
ls -l shows details that differ on every machine
(owner, dates, sizes), so here is an illustrative listing of a home
directory, with -a added:
$ ls -la
total 32 drwxr-x--- 4 ana ana 4096 Sep 30 14:02 . drwxr-xr-x 4 root root 4096 Sep 2 09:15 .. -rw------- 1 ana ana 1284 Sep 30 13:58 .bash_history -rw-r--r-- 1 ana ana 220 Sep 2 09:15 .bash_logout -rw-r--r-- 1 ana ana 3771 Sep 2 09:15 .bashrc -rw-r--r-- 1 ana ana 807 Sep 2 09:15 .profile drwx------ 2 ana ana 4096 Sep 2 09:20 .ssh lrwxrwxrwx 1 ana ana 19 Sep 28 10:40 current -> projects/website-v2 drwxr-xr-x 4 ana ana 4096 Sep 28 10:39 projects
This listing and the examples on this page are shown in byte order,
as with LC_ALL=C.UTF-8. In English locales,
ls, glob sorting and sort ignore dots,
dashes and case at first, so your order may differ (here,
.profile would come after current);
LC_ALL=C gives byte order.
Reading one line from left to right:
| Column | Example | Meaning |
|---|---|---|
| Type and permissions | drwxr-xr-x |
First character the file type (below), then read, write and execute for owner, group and others (see Users, Permissions and Access Control) |
| Link count | 4 |
Number of hard links (for a directory: 2 plus its number of subdirectories) |
| Owner and group | ana ana |
Who owns the file, and its group |
| Size | 4096 |
In bytes (with -h: 4.0K); for a
directory, the size of the directory entry list, not of its
contents
|
| Modification time | Sep 28 10:39 |
Last change of the contents; older files show the year instead of the time |
| Name | current -> projects/website-v2 |
For a symbolic link, also where it points |
total 32 on the first line is the disk space the listed
files use, in blocks of 1 KiB. To see how much a whole directory
holds, use du -sh (see
Logs and Monitoring).
Hidden files
A hidden file is a file or
directory whose name starts with a dot, such as
.bashrc or .ssh. There is no special
attribute: ls and file managers simply leave these
names out unless you ask for them. The convention keeps personal
settings ("dotfiles") out of the way in your home directory.
mkdir settings && cd settings
touch .bashrc .profile notes.txt
mkdir .config
echo "ls:"; ls -1
echo "ls -a:"; ls -1a
echo "ls -A:"; ls -1A
ls: notes.txt ls -a: . .. .bashrc .config .profile notes.txt ls -A: .bashrc .config .profile notes.txt
The tree command
tree draws a directory and everything below it as a
tree. It is not installed by default on most servers; install it
with sudo apt install tree (Debian, Ubuntu) or
sudo dnf install tree (Fedora, RHEL).
$ tree projects
projects
├── notes
│ └── todo.txt
└── website
├── css
│ └── style.css
└── index.html
3 directories, 3 files
tree -L 2 limits the depth to two levels, and
tree -a includes hidden files. Where
tree is missing, ls -R or
find gives the same information as a plain list.
File types
Linux does not use the extension to decide what a file is:
.txt or .sh is only part of the name, a
hint for people. The file system does record a few basic types, and
the first character of ls -l shows them:
| Character | Type | Example |
|---|---|---|
- |
Regular file (text, program, image, anything) | /etc/hostname |
d |
Directory | /etc |
l |
Symbolic link | /bin on current systems |
c |
Character device (data as a stream) | /dev/null, /dev/tty |
b |
Block device (storage) | /dev/sda, /dev/nvme0n1 |
s |
Socket (communication between processes) | /run/systemd/notify |
p |
Named pipe (FIFO, first in, first out) | Created with mkfifo |
stat -c '%F' prints the type in words, and the
file command goes further: it looks inside
the file to recognize its contents.
mkdir demo && cd demo
mkdir scripts
echo "Meeting at 10:00" > notes.txt
printf '#!/bin/bash\necho "Backup done"\n' > scripts/backup.sh
ln -s notes.txt latest
stat -c '%F: %n' scripts notes.txt latest /dev/null
file notes.txt scripts scripts/backup.sh latest
directory: scripts regular file: notes.txt symbolic link: latest character special file: /dev/null notes.txt: ASCII text scripts: directory scripts/backup.sh: Bourne-Again shell script, ASCII text executable latest: symbolic link to notes.txt
file recognized the script by its first line, not by
.sh. That makes it the right tool for an attachment
called invoice.pdf that might not be a PDF (Portable
Document Format) file at all. The exact wording can differ slightly
between versions of file. Links are explained in
Working with Files and Directories.
Spaces and special characters in names
Linux allows almost any character in a file name: only
/ (the separator) and the null character are forbidden.
Names with spaces, brackets or a leading dash are legal, but the
shell treats those characters specially, so you must quote or escape
them:
mkdir "Project Files"
cd "Project Files"
pwd | sed "s|$HOME|~|"
cd ..
cd Project\ Files # a backslash escapes one character
touch -- -draft.txt 'budget (final).ods'
ls -1
ls -1 ./-draft.txt
~/Project Files -draft.txt budget (final).ods ./-draft.txt
-
Spaces: put the name in quotes or escape each
space with
\. Tab completion does the escaping for you. -
Brackets,
*,?,$,&,;: all mean something to the shell; quote the name. -
A leading dash: a name such as
-draft.txtlooks like an option.ls -draft.txtfails with "invalid option". Write./-draft.txt, or put--before it:--tells most commands that no more options follow.