courses

Shell and Other Basics

How the shell turns what you type into action

The shell is the command-line interpreter that reads your input and hands it to the kernel. Understanding how it resolves commands (PATH), manages the environment, handles input and output (redirects and pipes), and escalates privilege (sudo) is what separates fumbling at a prompt from working fluently — and several of these mechanisms are security-relevant in their own right. The commands you navigate and edit with are all run through the shell.

This page covers PATH resolution, environment variables, the help system, I/O redirection, and the super-user tools (sudo/su) — the last of which connects directly to access control and the privilege-escalation paths attackers hunt for.

Command Path

When you type a command, the shell searches a list of directories to find the executable. This list is stored in the PATH environment variable.

How PATH works

echo $PATH
# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

Directories are separated by :. The shell searches left to right and runs the first match it finds.

Viewing and modifying PATH

echo $PATH                       # current search path
which python3                    # which executable would run
type ls                          # show how shell resolves 'ls' (alias, builtin, file)
command -v python3               # POSIX-compliant alternative to which

# Add a directory to PATH (for current session only)
export PATH="$HOME/bin:$PATH"

# Add permanently — append to your interactive shell's startup file.
# Kali's default shell is zsh, so use ~/.zshrc; on bash systems use ~/.bashrc.
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc                  # reload without logging out

ℹ️ The examples below write to ~/.zshrc because the course VM (Kali) ships zsh as the default interactive shell. If your shell is bash, substitute ~/.bashrc — the export/source mechanics are identical.

Locating executables

whereis ls          # find binary, source, and man page
locate sshd         # fast filesystem-wide search (uses database)
find /usr -name python3 -type f   # find without database

Worked example — add a local scripts directory to PATH:

mkdir -p ~/bin
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
# Now scripts placed in ~/bin are available anywhere

⚠️ PATH order is a security boundary. The shell runs the first match, so a writable directory early in PATH — or the current directory . — lets an attacker shadow a real command (ls, sudo) with a malicious one. Never put . in PATH, and never make a PATH directory world-writable. This is a classic privilege-escalation vector.

Environment Variables

Environment variables are key-value pairs inherited by every child process. They control the behavior of the shell and programs.

Reading variables

echo $HOME              # home directory
echo $USER              # current username
echo $SHELL             # current shell
echo $PWD               # current directory
echo $LANG              # locale setting
env                     # list all environment variables
printenv HOME           # print a specific variable

Common environment variables

Variable Meaning
PATH Colon-separated list of executable search directories
HOME Current user’s home directory
USER Current username
SHELL Path to the user’s shell
LANG / LC_ALL Locale and character encoding
EDITOR Default text editor for programs that open one
PAGER Default pager (typically less)
TERM Terminal type
PS1 Shell prompt string
TMPDIR Temporary file directory

Setting and exporting variables

# Set for current shell only
MYVAR="hello"

# Export to make available to child processes
export MYVAR="hello"

# Set for one command only
EDITOR=vim git commit

# Unset a variable
unset MYVAR

# Set at login (persists across sessions)
echo 'export EDITOR=vim' >> ~/.zshrc

Variable expansion

name="world"
echo "Hello, $name"        # Hello, world
echo "Hello, ${name}!"     # Hello, world! (braces delimit variable name)
echo "${name:-default}"    # use 'default' if name is unset or empty
echo "${#name}"            # length of variable value
echo "${name^^}"           # uppercase (bash 4+)

Worked example — run a command with a modified environment:

# Run a command with a clean environment
env -i HOME=/tmp PATH=/usr/bin:/bin bash --norc

# Run a program with one extra variable
DEBUG=1 ./myscript.sh

Command Help

Linux has several layers of built-in documentation.

man — manual pages

man ls              # manual page for ls
man 5 passwd        # section 5 (file formats) for passwd
man -k keyword      # search man page names (like apropos)
man -K keyword      # full-text search across all man pages

Man page sections:

Section Content
1 User commands
2 System calls
3 Library functions
4 Special files (/dev)
5 File formats and configuration
6 Games
7 Miscellaneous (conventions, signals)
8 System administration commands

info — GNU info pages

info coreutils      # info page for coreutils package
info bash           # comprehensive bash documentation

Built-in help

ls --help           # GNU coreutils style
bash --help         # shell help
help cd             # help for bash builtins
help                # list all bash builtins
type -a ls          # all definitions of 'ls' (alias, function, path)

apropos and whatis

apropos compress        # search descriptions of all man pages
whatis tar              # one-line description

Worked example — find the right tool for a task:

# Find commands related to "network"
man -k network | grep -i interface
apropos socket | head -10

Redirects

Redirects control where command input comes from and where output goes.

Standard streams

Every process has three standard file descriptors:

Descriptor Name Default
0 stdin Keyboard
1 stdout Terminal
2 stderr Terminal

Output redirection

ls -l > files.txt           # redirect stdout to file (overwrite)
ls -l >> files.txt          # redirect stdout, appending
ls /bad 2> errors.txt       # redirect stderr to file
ls /bad 2>> errors.txt      # append stderr
ls -l /bad > out.txt 2>&1   # redirect both stdout and stderr to file
ls -l /bad &> out.txt       # bash shorthand for above
ls -l /bad 2>/dev/null      # discard stderr (send to null device)

Input redirection

sort < names.txt            # read stdin from file
wc -l < file.txt            # count lines in file via stdin

Here documents and here strings

# Here document: feed multiple lines to a command
cat << EOF
line one
line two
EOF

# Here string: feed a single string to stdin
grep "pattern" <<< "some string to search"

# Here document to a file
cat << 'EOF' > /tmp/script.sh
#!/bin/bash
echo "Hello"
EOF

Pipes

Pipes (|) connect stdout of one command to stdin of the next.

ps aux | grep nginx             # find nginx processes
ls -lh | sort -k5 -rh          # list files sorted by size
cat /var/log/syslog | grep -i error | tail -20

Worked example — capture both output and errors, view live and save:

# Run a build, show output on terminal AND save to log file
make 2>&1 | tee build.log

# Run command, discard all output
./noisycmd &>/dev/null

Super User

The root account (UID 0) has unrestricted access to the system. Most distributions restrict direct root login and require sudo for privileged operations.

sudo — run commands as another user

sudo command                    # run as root
sudo -u alice command           # run as user alice
sudo -i                         # start interactive root shell (login shell)
sudo -s                         # start root shell without login profile
sudo !!                         # repeat last command as root (bash history)
sudo -l                         # list allowed sudo commands for current user
sudo visudo                     # safely edit /etc/sudoers

su — switch user

su -            # switch to root (full login environment)
su - alice      # switch to alice's environment
su alice        # switch to alice, keep current environment
exit            # return to previous user

sudoers configuration

/etc/sudoers defines who can use sudo and for what. Always edit with visudo, which validates syntax before saving.

# Allow user 'alice' to run all commands as root
alice ALL=(ALL:ALL) ALL

# Allow group 'devops' to restart nginx without password
%devops ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx

# Allow user 'bob' to run specific commands
bob ALL=(ALL) /usr/bin/apt, /usr/bin/systemctl

Common sudoers patterns:

Rule fragment Meaning
ALL=(ALL:ALL) ALL All hosts, all users/groups, all commands
NOPASSWD: /path/cmd No password required for that command
%groupname Applies to all members of the group

Root shell via sudo

sudo su -           # switch to root via sudo
sudo bash           # start root bash shell
sudo -i             # login shell as root
whoami              # verify current user
id                  # show UID, GID, and groups

Worked example — safely grant a user limited sudo access:

# Edit sudoers safely
sudo visudo

# Add this line to let 'deploy' restart services only:
# deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart *, /bin/systemctl stop *

⚠️ Prefer sudo over a permanent root shell. sudo usage is logged to /var/log/auth.log (Debian/Ubuntu/Kali) or /var/log/secure (RHEL/Fedora), giving accountability — the A in AAA from security principles. Direct root logins leave a weaker audit trail, and an overly broad sudoers rule (especially NOPASSWD on a command that can spawn a shell) is one of the most common privilege-escalation findings — which is why attackers run sudo -l first thing.

Key takeaways

References


Related course pages: Navigation Basics · Editing Files · Working with Files · User Management · Access Control and Authorization · Host Security

🛠️ Maintenance note: shell mechanics are stable. The examples target Kali’s default zsh (~/.zshrc); if the course VM’s default shell changes, update those rc-file paths. Also watch the sudo log path, which differs by distro family.