How to Customise Your Linux Shell (Aliases, a Better Prompt, History That Works and Zsh or Fish)

You type docker compose logs -f forty times a week. Your prompt looks the same on the server and the Pi, which is how rm -r happens on the wrong box. Ctrl + R can't find the command you ran last Tuesday because another window overwrote the history. Twenty lines in ~/.bashrc fix all three, and they travel to every machine with the Ansible baseline (139).
Quick reference
| Want | Where |
|---|---|
| Aliases and functions | ~/.bash_aliases (sourced by Ubuntu's .bashrc) |
| A prompt with host, folder, git branch | PS1 in .bashrc, or Starship |
| History across sessions, no duplicates, with timestamps | HISTSIZE, HISTCONTROL, shopt -s histappend |
| Safer rm, cp, mv | alias rm='rm -I' etc. |
| A better shell | Zsh + Oh My Zsh, or Fish |
| Apply | source ~/.bashrc |
1. Aliases
nano ~/.bash_aliases
# navigation and listing
alias ll='ls -alFh --color=auto'
alias ..='cd ..'
alias ...='cd ../..'
# safety
alias rm='rm -I'
alias cp='cp -i'
alias mv='mv -i'
# docker
alias dc='docker compose'
alias dcl='docker compose logs -f --tail 100'
alias dcu='docker compose up -d'
alias dcd='docker compose down'
alias dps='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
# system
alias update='sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y'
alias ports='sudo ss -tlnp'
alias disk='df -h | grep -v tmpfs'
alias jl='journalctl -u'
alias check='uptime; systemctl --failed; df -h | grep -v tmpfs; free -h; docker ps --format "{{.Names}}\t{{.Status}}"'
# functions take arguments
mkcd() { mkdir -p "$1" && cd "$1"; }
extract() { case "$1" in *.tar.gz|*.tgz) tar xzf "$1";; *.zip) unzip "$1";; *.tar.xz) tar xJf "$1";; *) echo "unknown";; esac; }
source ~/.bashrc. dcl in any compose folder tails that stack; check is tutorial 80's five-minute version. rm -I asks once before deleting more than three files or a folder — enough to catch a typo, not enough to annoy.
2. A prompt that tells you where you are
Append to ~/.bashrc:
parse_git_branch() { git branch 2>/dev/null | sed -n 's/^\* //p' | sed 's/^/ (/;s/$/)/'; }
if [ "$(id -u)" -eq 0 ]; then C='\[\e[1;31m\]'; else C='\[\e[1;32m\]'; fi
PS1="${C}\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[1;33m\]\$(parse_git_branch)\[\e[0m\]\$ "
Green john@homeserver, blue folder, yellow git branch; the name turns red when you're root. Give each machine a distinct hostname (tutorial 9) and a different colour in C if you like — the Pi in cyan, the VPS in magenta. You stop running things on the wrong box.
For a prettier, faster, cross-shell prompt: curl -sS https://starship.rs/install.sh | sh, then eval "$(starship init bash)" in .bashrc. It shows git status, Docker context, and command duration out of the box.
3. History that actually works
HISTSIZE=100000
HISTFILESIZE=200000
HISTCONTROL=ignoreboth:erasedups
HISTTIMEFORMAT='%F %T '
shopt -s histappend
PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
Every session appends instead of overwriting, duplicates collapse, timestamps show when you ran something, and Ctrl + R reaches back a hundred thousand commands. history | grep certbot becomes a reliable record of what you did last year.
4. Small defaults worth setting
shopt -s autocd # type a folder name to cd into it
shopt -s cdspell # fix minor typos in cd
export EDITOR=nano # what sudoedit, git and crontab -e open
set -o noclobber # > won't overwrite an existing file (use >| to force)
And sudo visudo → Defaults timestamp_timeout=30 so sudo asks every 30 minutes instead of 15.
5. Zsh or Fish?
- Zsh with Oh My Zsh: autocompletion menus, plugins (git, docker, zsh-autosuggestions, zsh-syntax-highlighting), themes. Bash-compatible, so scripts and habits transfer. sudo apt install zsh && chsh -s $(which zsh).
- Fish: the nicest defaults with zero config — autosuggestions, highlighting and completions built in — but not bash-compatible (different syntax for scripts and some aliases).
For a server you mostly SSH into, well-configured bash is fine and is what every tutorial assumes. Try Zsh on your laptop first; if you like it, bring it to the server. Keep /bin/bash for root and scripts either way.
6. Take it everywhere
Put .bashrc, .bash_aliases and .tmux.conf in a dotfiles repo on Gitea (45), and have the Ansible base role (140) copy them to every host. A new machine then feels like home at the first prompt.
Troubleshooting
Aliases don't work in scripts or cron. Correct — aliases are interactive only. Use full commands or functions in scripts.
Prompt shows raw escape codes. Missing [ and ] around colour sequences; copy the line exactly.
History still missing commands. histappend not set, or two shells started before the change; new sessions behave.
rm -I not asking. Fewer than four files; that's by design. Use rm -i for always.
Wrapping up
A .bash_aliases with the commands you actually type, a prompt that names the host and turns red as root, history that keeps everything, three safer defaults. Twenty minutes, then the same file on every machine via git and Ansible — and the terminal stops costing you keystrokes and mistakes.
