Chapter 1: Command-Line Fundamentals (CLI)
Unix was originally created so everyone would have the right to use a computer, and from it the GNU/Linux kernel emerged, spawning several families. Their differences lie in their repositories and software, including their desktop environments. Repositories are collections of installable software for an operating system. These distributions form a wide spectrum of operating systems, the most well-known of which are:
- Debian
- Ubuntu
- Kali Linux
- Red Hat
- Fedora
- CentOS
- Slackware
- FreeBSD
- OpenSUSE
On servers, there is usually no Graphical User Interface (GUI), and all administrative operations are performed via SSH and the terminal. Therefore, the CLI is not an option but the only way to work with a server.
1.1 Structure of a Linux Command
Every Linux command consists of three parts:
command -options arguments
| | |
command switches inputs
Example:
ls -la /home
ls→ the command (list)-la→ switches (l = long format, a = all files)/home→ argument (the path to list)
Switches are written with - (short) or -- (long):
ls -a # short
ls --all # long
ls -la # multiple combined switches
1.2 Linux Filesystem Structure
Unlike Windows, where drives (C:, D:) are defined separately, Linux has a
single tree starting from / (the root).
| Path | Purpose |
|---|---|
/ |
Root of the entire filesystem |
/home |
Home directories for regular users |
/root |
Home directory for the root user |
/etc |
Configuration files |
/var |
Logs and variable data |
/tmp |
Temporary files |
/bin, /usr/bin |
Executable commands |
/dev |
Devices (disks, USB, etc.) |
/proc, /sys |
Kernel and system information |
Absolute vs. relative paths:
/etc/nginx/nginx.conf # absolute (starts with /)
./config.txt # relative (from the current path)
../logs/ # relative (one level up)
Special symbols:
.→ current directory..→ parent directory~→ user's home directory (/home/username)
1.2.1 Basic Navigation Commands
pwd — Print the current path
pwd
# /home/ali
ls — List directory contents
ls # simple listing
ls -l # detailed listing
ls -a # show hidden files
ls -lh # human-readable sizes (KB, MB)
ls -la /etc # combined options on a specific path
ls -R # recursive
Output of ls -l:
-rw-r--r-- 1 ali ali 4096 Sep 11 10:30 file.txt
│└┬┘└┬┘└┬┘ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ └─ file name
│ │ │ │ │ │ │ │ └─ date
│ │ │ │ │ │ │ └─ size
│ │ │ │ │ │ └─ group
│ │ │ │ │ └─ owner
│ │ │ │ └─ link count
│ └──┴──┴─ permissions
└─ file type (- file, d directory, l link)
cd — Change directory
cd /etc # go to an absolute path
cd nginx # go to a directory inside the current path
cd .. # go one level up
cd ~ # go to home directory
cd - # return to the previous directory
cd # go to home directory
1.2.2 File and Directory Management
Creating
touch file.txt # create an empty file
mkdir mydir # create a directory
mkdir -p a/b/c # create nested directories
Copying
cp file.txt /tmp/ # copy a file
cp -r mydir /tmp/ # recursively copy a directory
cp -v file.txt backup.txt # show details
Moving and renaming
mv file.txt /tmp/ # move
mv old.txt new.txt # rename
Deleting
rm file.txt # delete a file
rm -r mydir # delete a directory
rm -rf mydir # force delete without confirmation (dangerous)
rm -i file.txt # delete with confirmation
Warning: the command
rm -rf /destroys the entire system. Never run this command with sudo or on/. The "r" stands for recursive — meaning ifmydiris a directory, its contents are also removed (files, subdirectories, everything).
1.2.3 Viewing File Content
cat file.txt # print the whole file
less file.txt # page-by-page view (q to quit)
head file.txt # first ten lines
head -n 20 file.txt # first twenty lines
tail file.txt # last ten lines
tail -f /var/log/syslog # follow the log live (Ctrl+C to exit)
1.2.3.1 Inspecting File Metadata with stat
The stat command displays the complete metadata of a file or directory: owner, group,
permission level, size, access and modification times, and link count. This tool is widely used in
security auditing and scripting.
stat file.txt # show full metadata
stat -c '%U:%G %a %n' file.txt # custom output: owner:group perms name
stat -c '%a' file.txt # octal permissions only
stat -c '%U' file.txt # owner name only
stat -c '%G' file.txt # group name only
# Inspect multiple files with one command
stat -c '%U:%G %a %n' /srv/app/deploy.sh /srv/app/lib.sh /srv/app/audit.sh
Sample output:
root:root 755 /srv/app/deploy.sh
root:root 755 /srv/app/lib.sh
root:root 755 /srv/app/audit.sh
This output is very useful for quickly auditing sensitive files on a server. The output can also be piped
to tools like awk or grep to identify files with unauthorized permissions:
find /srv -type f -exec stat -c '%a %n' {} \; | grep -E '^77[0-7]'
1.2.4 Searching
find — Searching for files
find /etc -name "*.conf" # .conf files
find / -type d -name "nginx" # directory named nginx
find /var/log -size +10M # files larger than 10MB
find /home -mtime -7 # modified in the past seven days
grep — Searching inside content
grep "error" /var/log/syslog # lines containing error
grep -i "error" file.txt # case-insensitive
grep -r "localhost" /etc/ # recursive search in a directory
grep -n "root" /etc/passwd # show line numbers
grep -v "comment" file.txt # lines NOT matching the pattern
1.2.5 Pipe and Redirection
Pipe (|) — Passing output of one command to the input of the next
ps aux | grep nginx # nginx processes
cat /etc/passwd | wc -l # number of users
ls -la | less # view the listing page by page
history | grep "apt" # apt commands in history
Redirection — Directing input and output
ls > files.txt # write output (overwrite)
ls >> files.txt # append output
cat < files.txt # use file as input
command 2> error.log # store errors only
command &> all.log # store output and errors
command > /dev/null # discard output
1.2.5.1 Chaining Commands with && and ||
In Linux, several commands can be chained together. The && operator runs the second
command only if the first exits successfully. The || operator does the opposite: it runs
the second command only if the first fails.
cd /var/log && ls -lh # only run ls if cd succeeded
apt update && apt upgrade # update then upgrade
command1 || echo "failed" # print a message if the first command failed
1.2.6 Text Editor (nano)
For server work, nano is the simplest option:
nano /etc/hosts
Common nano shortcuts:
Ctrl + O→ saveCtrl + X→ exitCtrl + W→ searchCtrl + K→ cut lineCtrl + U→ paste
For more advanced work, learning vim is recommended, but nano is enough to get started.
1.2.7 Getting Help
man ls # full manual
ls --help # concise help
man -k "copy" # search the manuals
apropos network # commands related to networking
Inside man: press q to quit, / to search, and n to
jump to the next match.
1.2.8 Terminal Tips
| Key | Function |
|---|---|
Tab |
Auto-complete a command or path |
Ctrl + C |
Interrupt a running command |
Ctrl + Z |
Suspend a command |
Ctrl + L |
Clear the screen |
Ctrl + R |
Search history |
↑ / ↓ |
Previous / next command |
history |
Show previous commands |
!! |
Re-run the last command |
Ctrl + A |
Move to the beginning of the line |
Ctrl + E |
Move to the end of the line |
1.2.9 Chapter 1 Practice
If you have access to an Ubuntu system (VM or server), try the following exercises:
# 1. Go to /var/log and list its contents
cd /var/log && ls -lh
# 2. Create a practice directory
mkdir ~/practice && cd ~/practice
# 3. Create three files
touch file1.txt file2.txt file3.txt
# 4. Add content
echo "hello linux" > file1.txt
echo "server admin" >> file1.txt
# 5. View the content
cat file1.txt
# 6. Search across all files
grep "linux" *.txt
# 7. Count lines in the passwd file
wc -l /etc/passwd
# 8. View the last five users on the system
tail -n 5 /etc/passwd
# 9. Clean up
cd ~ && rm -rf practice
1.2.10 Chapter 1 Learning Checklist
- The difference between absolute and relative paths
- Working with
ls,cd,pwd,mkdir,touch,cp,mv, andrm - Viewing files with
cat,less,head, andtail - Searching with
findandgrep - Using pipes (
|) and redirection (>,>>) - Working with the
nanoeditor - Getting help with
manand--help - Keyboard shortcuts (Tab, Ctrl+C, Ctrl+R)
1.3 Essential Command-Line Tools
Below are tools you will need in the daily administration of any Linux server.
1.3.1 lsb_release — Identifying the Distribution
The lsb_release command displays version information for the Linux distribution. Its name
comes from LSB (Linux Standard Base), a standard defined to unify the structure of Linux
distributions. This command is one of the first tools used on any new server to precisely identify the
distribution and its version.
lsb_release -a # show all distribution information
lsb_release -d # description only
lsb_release -r # release number only
lsb_release -c # codename only
lsb_release -i # distributor ID only
lsb_release -s -r # release only, without label (script-friendly)
Sample output on Ubuntu 22.04:
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.3 LTS
Release: 22.04
Codename: jammy
| Field | Meaning |
|---|---|
Distributor ID |
Distribution name (Ubuntu, Debian, CentOS, etc.) |
Description |
Full version description |
Release |
Version number (e.g., 22.04) |
Codename |
Codename of the release (e.g., jammy, focal, noble) |
The message "No LSB modules are available" is normal and simply means the lsb-core package
is not installed. To silence it: sudo apt install lsb-core.
Use in scripts to branch based on the OS version:
#!/bin/bash
VERSION=$(lsb_release -rs)
if [ "$VERSION" = "22.04" ]; then
echo "This script targets Ubuntu 22.04"
else
echo "Unsupported version: $VERSION"
exit 1
fi
If lsb_release is not available on the system, the following alternatives can be used:
cat /etc/os-release # newer standard, available on most distributions
cat /etc/issue # simple and quick
hostnamectl # system and distribution info
uname -a # kernel info (not distribution)
1.3.2 ping — Testing Network Connectivity
The ping tool tests reachability of a host and measures response time.
ping google.com # test connectivity to a host
ping -c 4 8.8.8.8 # send four packets and stop
1.3.3 ip — Network Management and Inspection
The ip tool is the modern replacement for ifconfig, used to view and configure
network interfaces.
ip a # show interfaces and addresses
ip r # show the routing table
ip link # link status
1.3.4 ss — Inspecting Connections and Ports
The ss tool is a faster replacement for netstat, used to view open ports and
active connections.
ss -tulnp # open ports and their processes
ss -tan # TCP connections
1.3.5 tcpdump — Network Traffic Analysis
tcpdump is a command-line packet analyzer for capturing and inspecting network packets.
Unlike ss, which only lists connections, tcpdump shows the actual traffic at
the packet level, making it invaluable for network troubleshooting, connection inspection, security
analysis, and service debugging.
Installation and execution require root privileges:
apt install tcpdump
sudo tcpdump -D # list capture-capable interfaces
Basic Structure and Key Switches
The tcpdump command combines output-control switches and a BPF (Berkeley Packet Filter)
expression:
| Switch | Function |
|---|---|
-i <iface> |
Select interface (e.g., eth0, tun0, any) |
-n |
Do not resolve IPs to hostnames (no DNS) |
-nn |
Do not resolve IPs or ports to service names |
-c <N> |
Capture only N packets and stop |
-w file.pcap |
Write to a file for later Wireshark analysis |
-r file.pcap |
Read from a saved file |
-A |
Print content as ASCII (for unencrypted traffic) |
-X |
Print content as HEX and ASCII |
-v / -vv / -vvv |
Increase output verbosity |
-s <N> |
Set snapshot length per packet (0 = full) |
Common Filters (BPF)
Filters come after the switches and are usually enclosed in quotes. Some common examples:
sudo tcpdump -ni eth0 # all traffic on eth0
sudo tcpdump -ni eth0 'host 8.8.8.8' # traffic with a specific host
sudo tcpdump -ni eth0 'tcp port 443' # HTTPS only
sudo tcpdump -ni eth0 'udp port 53' # DNS only
sudo tcpdump -ni eth0 'src 192.168.1.10' # packets originating from this IP
sudo tcpdump -ni eth0 'dst 192.168.1.10' # packets sent to this IP
sudo tcpdump -ni eth0 'net 192.168.1.0/24' # an entire subnet
sudo tcpdump -ni eth0 'tcp[tcpflags] & tcp-syn != 0' # SYN packets only
Filters can be combined with and, or, and not:
sudo tcpdump -ni tun0 -nn 'host 192.168.9.101 and tcp port 443'
sudo tcpdump -ni eth0 'not port 22 and not arp'
Practical Examples
1. Checking connectivity to a specific server on port 443:
sudo tcpdump -ni any -nn 'host 192.168.9.101 and tcp port 443'
Sample output:
14:23:01.123456 IP 192.168.9.50.54321 > 192.168.9.101.443: Flags [S], seq ...
14:23:01.123789 IP 192.168.9.101.443 > 192.168.9.50.54321: Flags [S.], ack ...
14:23:01.124001 IP 192.168.9.50.54321 > 192.168.9.101.443: Flags [.], ack ...
If you only see Flags [S] (SYN) and no reply [S.] comes back, the connection is
blocked by a firewall or the destination server.
2. Saving traffic for Wireshark analysis:
sudo tcpdump -ni eth0 -s 0 -w capture.pcap 'tcp port 80'
Then transfer capture.pcap to your local machine and open it in Wireshark. The
-s 0 switch captures the full packet (no truncation).
3. Viewing HTTP request content (unencrypted):
sudo tcpdump -ni eth0 -A 'tcp port 80 and host example.com'
Headers and body of the request are shown as ASCII. Note that this only works for unencrypted traffic (plain HTTP) and has no effect on HTTPS.
4. Inspecting DHCP and DNS:
sudo tcpdump -ni eth0 -vv 'port 67 or port 68' # DHCP
sudo tcpdump -ni eth0 'udp port 53' # DNS
5. VPN troubleshooting:
sudo tcpdump -ni tun0 -nn 'host 10.8.0.1'
To verify that traffic really travels through the VPN tunnel. Common VPN interfaces include
tun0, wg0 (WireGuard), and ppp0.
Security and Performance Notes
- On high-traffic servers, running
tcpdumpwithout a filter can strain CPU and disk. Always use a filter and the-cswitch. - HTTPS traffic is encrypted, so
tcpdumponly shows metadata (IP, port, flags, size). - Captured traffic may contain sensitive information. Keep
.pcapfiles secure and delete them after analysis. - In production environments, prefer tools like
tsharkortermsharkovertcpdumpfor easier analysis. - For more complex filters, consult the official
pcap-filterdocumentation:man pcap-filter.
Combining with Other Tools
tcpdump output can be piped to other tools:
# Count packets sent to a specific IP
sudo tcpdump -ni eth0 -c 100 'dst 8.8.8.8' 2>/dev/null | wc -l
# Save to a compressed file for transfer
sudo tcpdump -ni eth0 -s 0 -w - 'port 443' | gzip > capture.pcap.gz
# Read from a file and search
sudo tcpdump -r capture.pcap -nn | grep '192.168.9.101'
1.3.6 curl and wget — Network Data Transfer
Both tools are used to download files, test APIs, and inspect server responses.
curl https://example.com # fetch content
curl -I https://example.com # headers only
curl -o file.zip https://example.com/f.zip # save to a file
curl -fsSL https://example.com/install.sh | bash # fetch and run
wget https://example.com/file.zip # download a file
wget -c https://example.com/file.zip # resume a partial download
1.3.7 Fixing Package Installation Errors
If package installation fails, the following commands can help:
sudo apt --fix-broken install # fix broken dependencies
sudo dpkg -i package.deb # install a downloaded .deb file
sudo apt install -f # complete installation after dpkg
1.3.8 rsync — Synchronization and Backup
The rsync tool copies and synchronizes files locally or remotely, and is highly efficient
for large transfers.
rsync -avz /home/ /backup/home/
rsync -avz --delete /src/ /dst/
rsync -avz -e ssh /home/ user@server:/backup/
1.3.9 tmux — Session Management
With tmux, you can keep an SSH session alive even after disconnection. This is critical for
running long commands.
apt install tmux
tmux new -s work # create a new session
tmux attach -t work # attach to a session
tmux ls # list sessions
Important tmux shortcuts (after pressing Ctrl+B):
d→ detach from the sessionc→ create a new windown/p→ next / previous window"→ horizontal split%→ vertical split
Chapter 2: User and Permission Management
This chapter covers creating and managing users, groups, and file permission levels.
2.1 User Management
useradd ali # create a user
useradd -m -s /bin/bash ali # create a user with a home directory and shell
passwd ali # set a password
usermod -aG sudo ali # add to the sudo group
userdel -r ali # delete the user and their home directory
2.2 Group Management
groupadd developers # create a group
groupdel developers # delete a group
groups ali # show the groups a user belongs to
id ali # if permitted: show full user information
id # show full information for the current user, e.g.:
# uid=1001(admin) gid=1001(admin) groups=1001(admin),27(sudo),100(users)
UID (User ID) is a unique numeric identifier for a user. The root user typically has UID=0.
GID (Group ID) is the numeric identifier of the user's primary group.
groups lists all groups a user belongs to (including the primary group and supplementary/secondary groups). Each group is shown in the format GID(Name).
Main uses of the id command:
- Checking permissions: to understand what privileges the current user has (e.g., whether they are in the sudo or wheel group).
- Permission troubleshooting: when a program cannot access a file,
idhelps verify whether the user is in the correct group. - Scripting: in shell scripts to check whether the script is running as root (e.g.,
if [ "$(id -u)" -eq 0 ]; then ...).
2.3 File Permission Levels
chmod 755 script.sh # set permissions
chmod u+x script.sh # add execute permission for the owner
chown ali:ali file.txt # change owner and group
2.3.1 Understanding Permission Output in ls -l
When you run ls -l, the first column shows the file type and permission levels:
-rwxr-xr-x 1 ali ali 4096 Sep 11 10:30 script.sh
│└┬┘└┬┘└┬┘
│ │ │ └── permissions for other users (others)
│ │ └───── permissions for the group (group)
│ └──────── permissions for the owner (user)
└────────── file type: - file, d directory, l link
Each three-character group is a combination of the following letters:
r→ readw→ writex→ execute-→ no permission
2.3.2 Octal (Numeric) Permission Notation
Each of the three permission characters can be represented by a number between 0 and 7, because each character has a binary value:
| Permission | Binary value | Numeric value |
|---|---|---|
--- |
000 | 0 |
--x |
001 | 1 |
-w- |
010 | 2 |
-wx |
011 | 3 |
r-- |
100 | 4 |
r-x |
101 | 5 |
rw- |
110 | 6 |
rwx |
111 | 7 |
So the permission rwxr-xr-x is equivalent to:
owner → rwx → 7
group → r-x → 5
others → r-x → 5
Result: 755
2.3.3 Common Permissions and Their Uses
| Number | Symbol | Purpose |
|---|---|---|
644 |
rw-r--r-- |
Regular files, web pages, configs |
600 |
rw------- |
Private files such as SSH keys (id_rsa) |
755 |
rwxr-xr-x |
Executable scripts, public directories |
700 |
rwx------ |
User-private directories (~/.ssh) |
777 |
rwxrwxrwx |
Full access for everyone — dangerous and insecure |
664 |
rw-rw-r-- |
Group files that group members can edit |
2.3.4 Changing Permissions with chmod
There are two ways to set permissions:
Numeric (Octal) mode:
chmod 755 script.sh # rwxr-xr-x
chmod 644 config.txt # rw-r--r--
chmod 600 ~/.ssh/id_rsa # rw-------
chmod -R 755 /var/www # recursive on a directory
Symbolic mode:
chmod u+x script.sh # add execute for owner
chmod g-w file.txt # remove write from group
chmod o-rwx secret.txt # remove all permissions from others
chmod a+r public.txt # add read for everyone (a = all)
chmod u=rw,g=r,o= file # set exact permissions
Symbolic letters:
u→ owner (user)g→ groupo→ othersa→ all (u+g+o)+→ add,-→ remove,=→ set exactly
2.3.5 Security Recommendations
- Never set
777on sensitive files. - Always use
600for private SSH keys, otherwise SSH refuses to connect. - Set the execute permission (
x) only on files that actually need to be executed. - For directories,
755or750is reasonable — not777. - After every change, verify the result with
ls -lorstat -c '%a %n' file.
Chapter 3: Package Management with APT
Ubuntu uses the apt tool to manage packages.
3.1 Basic APT Commands
apt update # update the package list
apt upgrade # upgrade installed packages
apt install nginx # install a package
apt remove nginx # remove a package
apt autoremove # remove unused packages
apt search nginx # search for a package
3.2 Repository Management
cat /etc/apt/sources.list
add-apt-repository ppa:example/ppa
Chapter 4: Service Management with systemd
Ubuntu uses systemd to manage services.
4.1 systemctl Commands
systemctl start nginx # start a service
systemctl stop nginx # stop a service
systemctl restart nginx # restart a service
systemctl enable nginx # enable at boot
systemctl status nginx # view status
4.2 Viewing Logs with journalctl
journalctl -u nginx # logs for a specific service
journalctl -f # follow live logs
journalctl --since "1 hour ago"
Chapter 5: Network Configuration
In recent versions of Ubuntu, network configuration is done through netplan.
5.1 Netplan Configuration File
nano /etc/netplan/01-netcfg.yaml
network:
version: 2
ethernets:
eth0:
dhcp4: true
netplan apply # apply configuration
5.2 Network Inspection Tools
ip a # show network addresses
ip r # show routing table
ss -tulnp # show open ports
ping google.com # test connectivity
Chapter 6: Firewall with UFW
ufw is a simple interface for managing the firewall on Ubuntu.
6.1 Basic UFW Commands
ufw enable # enable the firewall
ufw status # view status
ufw allow 22 # allow the SSH port
ufw allow 80/tcp # allow HTTP
ufw deny 23 # block a port
ufw delete allow 80 # remove a rule
Chapter 7: Storage Management
Managing disks, partitions, and filesystems is an essential server administration skill.
7.1 Viewing Disks
lsblk # show disks and partitions
df -h # Disk Free - show disk usage
du -sh /var/log # directory size
7.2 Partitioning and Mounting
fdisk /dev/sdb # partition a disk
mkfs.ext4 /dev/sdb1 # create a filesystem
mount /dev/sdb1 /mnt # mount
For permanent mounting, add an entry to /etc/fstab.
Chapter 8: Web Servers and Common Services
This chapter covers installation and configuration of common server services.
8.1 Installing Nginx
apt install nginx
systemctl enable --now nginx
8.2 Configuring a Virtual Host
nano /etc/nginx/sites-available/example.com
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
8.3 SSL Certificate with Certbot
apt install certbot python3-certbot-nginx
certbot --nginx -d example.com
Chapter 9: Containers and Virtualization
Containers provide a lightweight way to run services.
9.1 Installing Docker
apt install docker.io
systemctl enable --now docker
9.2 Basic Docker Commands
docker run -d -p 80:80 nginx
docker ps
docker stop <container_id>
docker logs <container_id>
Chapter 10: Automation and Security
Automating repetitive tasks and following security best practices close out this guide.
10.1 Shell Scripting
#!/bin/bash
echo "Starting backup..."
tar -czf /backup/home-$(date +%F).tar.gz /home
echo "Backup completed."
10.1.1 Bash Fundamentals — Variables, Conditions, and Loops
Bash (Bourne Again Shell) is the default shell on most Linux distributions, including Ubuntu. Any script
that begins with #!/bin/bash is interpreted by Bash. The following sections cover the most
important Bash concepts.
Shells and How They Differ from Bash
A shell is a layer between the user and the operating system kernel that interprets and executes your commands. Bash is one of the most common shells, but it is not the only one. Depending on the operating system and user preference, different shells may be installed and used:
| Shell | Origin / Distribution | Characteristic |
|---|---|---|
sh |
Bourne Shell — the original Unix shell | Simple and standard, the ancestor of many shells |
bash |
GNU — default on most Linux distributions | An evolved sh with features like history and tab completion |
zsh |
Default on macOS since Catalina | More advanced features, extensible with Oh My Zsh |
fish |
Friendly Interactive Shell | Simpler syntax, smart auto-completion |
dash |
Used as system sh on some distros like Debian | Lightweight and fast, ideal for system scripts |
ksh |
Korn Shell — older, used on Unix and Mac | Ancestor of some bash and zsh features |
Unix-like systems such as macOS use zsh by default, and classic shells like ksh
and sh are also available there. On Ubuntu servers, the default shell is bash.
To view the current shell and the installed shells:
echo $SHELL # the user's current shell
cat /etc/shells # allowed shells on the system
chsh -s /bin/zsh # change the user's default shell (optional)
Important: scripts starting with #!/bin/bash are always run by bash, even if your
interactive shell is zsh or fish. However, scripts starting with
#!/bin/sh may point to different shells on different distributions (for example,
dash on Ubuntu and bash on macOS). For complex scripts, it is therefore
recommended to write #!/bin/bash explicitly.
Bash Startup Files — .bashrc and .bash_profile
When you open a new terminal or log in to the system, Bash automatically reads a set of configuration files. These files are where you define aliases, custom functions, environment variables, and shell appearance settings.
| File | When it runs | Main purpose |
|---|---|---|
/etc/profile |
At user login | Global settings for all users |
/etc/bash.bashrc |
Each time an interactive shell opens | Global Bash settings |
~/.bash_profile |
At login (login shell) | Environment variables, sourcing .bashrc |
~/.bashrc |
Each time a new interactive shell opens | Aliases, functions, prompt, appearance |
~/.bash_logout |
At logout | Cleanup, closing sessions |
To edit your personal file:
nano ~/.bashrc # edit shell settings
source ~/.bashrc # apply changes without closing the terminal
Example of useful settings in ~/.bashrc:
# aliases for common commands
alias ll='ls -alF'
alias update='sudo apt update && sudo apt upgrade'
alias ports='ss -tulnp'
# add a personal path to PATH
export PATH="$HOME/bin:$PATH"
# custom environment variable
export EDITOR=nano
# colored prompt with git branch
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\u@\h \w\[\033[32m\]\$(parse_git_branch)\[\033[00m\] $ "
Important note on the difference between .bashrc and .bash_profile: on Ubuntu,
.bash_profile usually does not exist, and only .bashrc is used. However, on
macOS and some other distributions, .bash_profile runs at login and typically sources
.bashrc inside it to avoid duplicating code:
# typical content of .bash_profile
if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi
Variables in Bash
In Bash, variables are defined with the = sign and no spaces. To read a variable's value,
prefix its name with $. Note that there must be no space on either side of =.
#!/bin/bash
NAME="Ali"
AGE=30
CITY="Tehran"
echo "Name: $NAME"
echo "Age: $AGE"
echo "City: ${CITY}"
# read-only (constant) variable
readonly PI=3.14
# special variables
echo "Script: $0" # script name
echo "First argument: " # first argument
echo "Argument count: $#" # number of arguments
echo "All arguments: $@" # all arguments
echo "Last exit code: $?" # exit status
echo "Script PID: $" # process ID
Note: variable names are case-sensitive. name and NAME are two different
variables. For arithmetic, use $(( )):
A=10
B=3
echo $((A + B)) # 13
echo $((A * B)) # 30
echo $((A / B)) # 3 (integer division)
echo $((A % B)) # 1 (remainder)
Conditions in Bash
The if structure in Bash looks like this. Conditions are written inside [ ]
(test) or [[ ]] (advanced test) and must be surrounded by spaces.
#!/bin/bash
FILE="/etc/passwd"
if [ -f "$FILE" ]; then
echo "File exists"
elif [ -d "$FILE" ]; then
echo "It is a directory"
else
echo "Not found"
fi
# numeric comparison
NUM=10
if [ "$NUM" -gt 5 ]; then
echo "Greater than 5"
fi
# string comparison
NAME="Ali"
if [ "$NAME" = "Ali" ]; then
echo "Hello Ali"
fi
Common operators in conditions:
| Operator | Meaning |
|---|---|
-f file |
File exists and is a regular file |
-d dir |
Directory exists |
-e path |
Path exists |
-r / -w / -x |
Readable / writable / executable |
-z str |
String is empty |
-n str |
String is non-empty |
-eq / -ne |
Equal / not equal (numbers) |
-gt / -lt |
Greater / less than (numbers) |
= / != |
Equal / not equal (strings) |
&& / || |
Logical AND / OR |
The case Structure
To choose among several options, case is much more readable than a chain of
if/elif:
#!/bin/bash
ACTION=""
case "$ACTION" in
start)
echo "Starting service..."
;;
stop)
echo "Stopping service..."
;;
restart)
echo "Restarting..."
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
Loops in Bash
for loop:
#!/bin/bash
# over a list
for FRUIT in apple banana orange; do
echo "Fruit: $FRUIT"
done
# over a numeric range
for i in {1..5}; do
echo "Number: $i"
done
# over files
for FILE in /var/log/*.log; do
echo "Processing $FILE"
done
# C-style
for ((i=0; i<10; i++)); do
echo "i=$i"
done
while loop:
#!/bin/bash
COUNT=1
while [ "$COUNT" -le 5 ]; do
echo "Count: $COUNT"
COUNT=$((COUNT + 1))
done
# read a file line by line
while IFS= read -r LINE; do
echo "Line: $LINE"
done < /etc/passwd
until loop: the opposite of while — it runs as long as the condition is false:
#!/bin/bash
COUNT=1
until [ "$COUNT" -gt 5 ]; do
echo "Count: $COUNT"
COUNT=$((COUNT + 1))
done
Functions in Bash
Functions make code cleaner and reusable:
#!/bin/bash
# function definition
greet() {
local NAME="" # local: local variable
echo "Hello $NAME!"
}
# function with a return value
add() {
echo $(( +
))
}
# function with error checking
check_root() {
if [ "$EUID" -ne 0 ]; then
echo "This script requires root privileges"
exit 1
fi
}
# calling
greet "Ali"
RESULT=$(add 5 3)
echo "Result: $RESULT"
check_root
Input and Output in Bash
#!/bin/bash
# read input from the user
read -p "Enter your name: " USER_NAME
echo "Hello $USER_NAME"
# read a password (hidden)
read -sp "Password: " PASSWORD
echo
# read with timeout
read -t 5 -p "Answer quickly: " ANSWER
# write to a file
echo "New text" >> /tmp/log.txt
# read from a file
while read -r LINE; do
echo "$LINE"
done < /tmp/log.txt
Error Handling in Bash
In serious scripts, error handling is critical. set -e makes the script exit on the first
error:
#!/bin/bash
set -euo pipefail
# -e : exit on error
# -u : error on undefined variable
# -o pipefail : error if any command in a pipe fails
# run a command and check for errors
if ! command -v nginx > /dev/null 2>&1; then
echo "nginx is not installed"
exit 1
fi
# trap: run a command on exit
cleanup() {
echo "Cleaning up..."
rm -f /tmp/tempfile
}
trap cleanup EXIT
Full Example: A Backup Script
Below is a complete, practical script that combines the concepts above:
#!/bin/bash
set -euo pipefail
# settings
BACKUP_DIR="/backup"
SOURCE_DIR="/home"
DATE=$(date +%F_%H-%M)
ARCHIVE="$BACKUP_DIR/home-$DATE.tar.gz"
KEEP_DAYS=7
# check root
if [ "$EUID" -ne 0 ]; then
echo "Please run with sudo"
exit 1
fi
# create the backup directory
mkdir -p "$BACKUP_DIR"
# create the backup
echo "Starting backup of $SOURCE_DIR ..."
tar -czf "$ARCHIVE" "$SOURCE_DIR"
echo "Backup created: $ARCHIVE"
# remove old backups
echo "Removing backups older than $KEEP_DAYS days..."
find "$BACKUP_DIR" -name "home-*.tar.gz" -mtime +$KEEP_DAYS -delete
# show disk usage
echo "Current backup usage:"
du -sh "$BACKUP_DIR"
Save this script somewhere like /usr/local/bin/backup.sh, make it executable with
chmod +x, and then schedule it in cron (see the next section).
10.2 Scheduling with Cron
crontab -e
# run daily at 3 AM
0 3 * * * /usr/local/bin/backup.sh
10.3 Automatic Security Updates
apt install unattended-upgrades
dpkg-reconfigure --priority=low unattended-upgrades
10.4 Backup with rsync
rsync -avz /home/ user@backup-server:/backup/
Chapter 11 (Optional): Specialized and Supplementary Tools
This chapter introduces tools that are good to know for server administration but are not strictly necessary. It is especially recommended for developers and those working with modern services.
11.1 tree — Directory Tree View
The tree tool shows the structure of folders and files as a tree — very useful for quickly
browsing project structure.
apt install tree # install the tool
tree # show the current path as a tree
tree /etc # show a specific path as a tree
tree -L 2 # only two levels deep
tree -d # directories only
tree -a # show hidden files
tree -h # show file sizes
tree -I "node_modules|.git" # ignore patterns
11.2 htop — Live Process Viewer
htop is an enhanced version of top with a better view of CPU, memory, and
processes.
apt install htop
htop
Useful keys in htop:
F3→ searchF4→ filterF5→ tree view of processesF6→ choose sort columnF9→ send a signal (kill)q→ quit
11.3 ncdu — Disk Usage Analysis
ncdu shows disk usage interactively and quickly, ideal for finding large directories.
apt install ncdu
ncdu /var
ncdu /home
11.4 jq — JSON Output Processing
jq reads, filters, and formats JSON data on the command line.
apt install jq
curl -s https://api.example.com/data | jq .
cat data.json | jq '.items[] | .name'
11.5 Node.js and npm Tools
To run JavaScript projects on a server, you need Node.js and npm:
node --version # Node.js version
npm --version # npm version
npm install # install project dependencies
npm install -g <package> # install a package globally
sudo npm install -g <package> # install globally with admin privileges
npx <command> # run a package directly without permanent install
npm install # install dependencies
npm run <script> # run a script defined in package.json
npm run build # build a production version
npm start # start the project
11.6 Docker Tools
Docker runs services in isolated environments, and managing it is an important server skill:
docker --version # Docker version
docker ps # running containers
docker ps -a # all containers
docker images # image list
docker volume ls # volume list
docker volume prune # remove unused volumes
docker system df # Docker disk usage
docker system prune # clean up unused resources
docker compose up # start services
docker compose up -d # start in the background
docker compose down # stop and remove services
docker compose ps # service status
docker compose logs # view logs
docker compose logs -f # follow live logs
docker exec -it <name> bash # enter a container
docker stop <name> # stop a container
docker start <name> # start a container
docker restart <name> # restart a container
docker rm <name> # remove a container
11.7 Git Tools
Git is used for version management and code deployment on servers:
git status # repository status
git branch # list branches
git checkout <branch> # switch branch (legacy)
git switch <branch> # switch branch (modern)
git add . # stage changes
git commit -m "message" # commit changes
git push # push to remote
git pull # pull from remote
git log # commit history
git diff # show changes
git remote -v # list remotes
11.8 Cloudflare Wrangler Tools
Wrangler is the Cloudflare CLI for managing and deploying Serverless projects:
npx wrangler # run Wrangler
npx wrangler --help # general help
npx wrangler observability --help # observability help
npx wrangler deploy # deploy the project
11.9 Python, pip, and uv Tools
For running Python projects and managing packages:
python3 --version # Python version
python3 # enter the Python interpreter
pip --version # pip version
pip3 install <package> # install a package
uv --version # uv version
uv run <script> # run a script with uv
uv tool install <tool> # install a global tool with uv
11.10 Other Useful Tools
| Tool | Purpose |
|---|---|
duf |
Nicely formatted disk usage output |
bat |
A cat replacement with syntax highlighting and line numbers |
ripgrep (rg) |
A faster grep replacement |
fd |
A simpler, faster find replacement |
fzf |
Interactive fuzzy search in lists and files |
lsof |
List files and open ports held by processes |
netstat / ss |
Inspect network connections |
htop / glances |
System monitoring |
fail2ban |
Automatic blocking of SSH attacks |