Freebie

Shell

The shell is the command-line interface between you and the operating system. Each user account is assigned a default shell, stored in the last field of /etc/passwd. System accounts are typically assigned /bin/false or /usr/sbin/nologin to prevent interactive login:

cat /etc/passwd | cut -d: -f 1,7
...
dhcpcd:/bin/false
sshd:/usr/sbin/nologin
testuser:/bin/bash

General command efficiency

Common keyboard shortcuts for navigating and editing commands at the prompt:

ShortcutDescription
Tab TabCycle through tab completion options
Ctrl + aMove cursor to beginning of line
Ctrl + eMove cursor to end of line
Ctrl + lClear the screen
Ctrl + kDelete from cursor to end of line
Ctrl + uDelete entire line (also clears passwords)
Ctrl + wDelete word to the left of cursor
Ctrl + rSearch command history
Ctrl + x eOpen current command in editor
echo $?View exit code of previous command

History

The shell records every command you run in ~/.bash_history. To prevent a command from being saved, prefix it with a space. On some distributions, add HISTCONTROL=ignoreboth to ~/.bashrc to enable this behavior:

CommandDescription
historyView bash history
!!Repeat the previous command
!<num>Run command number <num> from history
history -d <num>Delete command number <num> from history
<space><cmd>Run a command without saving it to history
HISTCONTROL=ignorebothAdd to ~/.bashrc to enable space-prefix behavior

Record a terminal session with script

The script command records everything printed to your terminal — commands and output — to a file. It starts a subshell. Everything you do in that subshell is captured until you type exit or press Ctrl+D.

Document a server build

Run script before setting up a new server. The output file becomes a full audit trail of every command and its result:

script ~/setup-$(hostname)-$(date +%F).log
# ... run your installation and configuration commands ...
exit

Capture a troubleshooting session to share

Record a session while diagnosing a problem, then share the file with a teammate who needs to reproduce the issue or review what you tried:

script /tmp/debug-nginx.log
systemctl status nginx
journalctl -u nginx --since "1 hour ago"
curl -I localhost
exit

Append to an ongoing log

Use -a to add to an existing file instead of overwriting it. Useful when a task spans multiple sessions:

script -a ~/change-window.log

Replay a session

Record timing data alongside the output, then replay the session at its original speed:

script --timing=timing.log session.log
# ... work ...
exit

scriptreplay --timing=timing.log session.log

Aliases

An alias substitutes a short command for a longer one. Add aliases to ~/.bashrc to make them permanent across sessions.

Manage aliases in the current session:

CommandDescription
aliasList all active aliases
unalias <name>Remove a specific alias
unalias -aRemove all aliases in the current session

For example, define aliases for common tasks:

alias cpu10='ps -L aux | sort -nr -k 3 | head -10'   # list top 10 CPU-consuming processes
alias mem10='ps -L aux | sort -nr -k 4 | head -10'   # list top 10 memory-consuming processes
alias lsmount='mount | column -t'                    # list mounted filesystems in columns

For a reference list of common aliases for system administration and penetration testing, see Best practices.

Scripts

A shell script is an executable text file containing commands that the shell runs in sequence. Scripts are useful for tasks you perform more than once. Two common shell constructs to know:

  • (( )): Arithmetic evaluation
  • $(): Run a command in a subshell and return its output to the current shell

Check for a file

Use [ ! -f <path> ] to test whether a file exists before running commands. For example, install Apache only if it is not already present:

#!/bin/bash
if [ ! -f /usr/sbin/apache2 ]; then
    sudo apt install -y apache2
    sudo apt install -y libapache2-mod-php8.1
    sudo a2enmod php8.1
    sudo systemctl restart apache2
fi

Check for a directory

Use [ ! -d <path> ] to test whether a directory exists:

#!/bin/bash
if [ ! -d /path/to/dir ]; then
    ...
fi

Variables

When bash sees a string followed by =, it assigns the value on the right to a variable. Variable names are case-sensitive and contain no spaces around =.

CommandDescription
envView all current shell variables
read <var>Read a line from STDIN and store it in <var>

Looping

Two loop types are available in bash:

  • while: Executes a block of commands until a condition is no longer true
  • for: Executes a block of commands once for every item in a set

A while loop that counts from 1 to 15:

#!/bin/bash
myvar=1
while [ $myvar -le 15 ]; do
    echo $myvar
    ((myvar++))
done

A for loop that iterates over a list of names:

#!/bin/bash
turtles="Donatello Leonardo Michelangelo Raphael"
for t in $turtles; do
    echo $t
done

Backup script

This script uses rsync to perform an incremental backup. It installs rsync if it is not present, then syncs /src to /target. Deleted files are moved to a dated subdirectory rather than removed permanently. Key options:

OptionDescription
-aArchive mode: retains file metadata
-vVerbose output
-bBackup mode: renames duplicate files in the target to prevent overwrites
--deleteRemove files from the target that no longer exist in the source
--backup-dirDirectory where deleted or overwritten files are moved
#!/bin/bash
curdate=$(date +%m-%d-%Y)

if [ ! -f /usr/bin/rsync ]; then
    sudo apt install -y rsync
fi

rsync -avb --delete --backup-dir=/backup/incremental/$curdate /src /target