CLI Productivity
Your terminal does not have to be a spartan black box. With the right tools and configuration, you can build an environment that is fast, informative, and a genuine pleasure to work in. This page covers tmux for session management, aliases and shell functions, fuzzy finding with fzf, fast searching with ripgrep, dotfile management, direnv for per-project environments, and the Starship prompt. Each of these tools is battle-tested and widely adopted in the professional developer community.
tmux: Terminal Multiplexer
tmux lets you run multiple terminal sessions inside a single window, detach from them, and reattach later. This is indispensable for remote work -- your session survives dropped SSH connections. It is also invaluable on your local machine for organizing workspaces.
Essential Commands
# Start a new named session
tmux new-session -s work
# Detach from the current session
# Press: Ctrl-b d
# List sessions
tmux list-sessions
# Reattach to a session
tmux attach -t work
# Split the current pane horizontally
tmux split-window -h
# Split vertically
tmux split-window -v
# Switch between panes
# Press: Ctrl-b <arrow key>
# Create a new window
# Press: Ctrl-b c
# Switch windows
# Press: Ctrl-b n (next) or Ctrl-b p (previous)
# Kill a session
tmux kill-session -t work
Configuring tmux
A .tmux.conf file in your home directory customizes tmux behavior. Here is a practical starter configuration that many developers use as a baseline:
# ~/.tmux.conf
# Change prefix from Ctrl-b to Ctrl-a (easier to reach)
unbind C-b
set -g prefix C-a
bind C-a send-prefix
# Enable mouse support
set -g mouse on
# Start window numbering at 1
set -g base-index 1
setw -g pane-base-index 1
# Sensible split bindings
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"
# Reload config
bind r source-file ~/.tmux.conf \; display "Config reloaded."
# Increase scrollback buffer
set -g history-limit 50000
# Reduce escape time (important for Vim users)
set -sg escape-time 10
# Use 256 colors
set -g default-terminal "screen-256color"
# Status bar customization
set -g status-position top
set -g status-interval 5
After editing, reload with tmux source-file ~/.tmux.conf or the prefix + r binding defined above.
Aliases
Aliases let you create shortcuts for commands you type frequently. Define them in your .bashrc or .zshrc.
# ~/.bashrc
alias ll='ls -la'
alias la='ls -A'
alias gs='git status'
alias gd='git diff'
alias gco='git checkout'
alias gp='git push'
alias gl='git log --oneline -20'
alias k='kubectl'
alias dc='docker compose'
alias ..='cd ..'
alias ...='cd ../..'
# Safety nets
alias rm='rm -i'
alias mv='mv -i'
alias cp='cp -i'
# Colorized output
alias grep='grep --color=auto'
alias diff='diff --color=auto'
Aliases are simple text substitution. For anything requiring arguments or logic, use a shell function instead. To temporarily bypass an alias, prefix the command with a backslash: \rm file.txt.
Shell Functions in .bashrc
Shell functions can accept arguments, use conditionals, and do everything a full script can do. Place them in your .bashrc or .zshrc for instant availability.
# Create a directory and cd into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Extract any archive format
extract() {
case "$1" in
*.tar.gz|*.tgz) tar xzf "$1" ;;
*.tar.bz2|*.tbz) tar xjf "$1" ;;
*.tar.xz) tar xJf "$1" ;;
*.zip) unzip "$1" ;;
*.gz) gunzip "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "Unknown format: $1" ;;
esac
}
# Quick HTTP server in the current directory
serve() {
local port="${1:-8000}"
echo "Serving on http://localhost:$port"
python3 -m http.server "$port"
}
# Find and kill a process by name
fkill() {
local pid
pid=$(ps aux | grep -i "$1" | grep -v grep | awk '{print $2}' | head -1)
if [[ -n "$pid" ]]; then
kill "${2:--15}" "$pid"
echo "Killed PID $pid"
else
echo "No matching process found."
fi
}
# Quick note-taking
note() {
local file="$HOME/notes/$(date +%F).md"
mkdir -p "$(dirname "$file")"
echo "- $(date +%T): $*" >> "$file"
echo "Note saved to $file"
}
fzf: Fuzzy Finder
fzf is an interactive fuzzy finder that integrates with your shell to supercharge file finding, history search, and more. It is one of the highest-impact tools you can add to your workflow.
# Install
# brew install fzf (macOS)
# apt install fzf (Debian/Ubuntu)
# After install, enable key bindings and completion
# $(brew --prefix)/opt/fzf/install (macOS)
Key Bindings
- Ctrl-R: Fuzzy search through command history. Type a few characters of any past command and fzf narrows the list instantly. This alone makes fzf worth installing.
- Ctrl-T: Insert a file path from the current directory tree.
- Alt-C: cd into a subdirectory.
**<Tab>: Trigger fzf completion in the middle of a command. For example,vim **<Tab>opens a fuzzy file picker.
# Preview files while searching
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow'
export FZF_CTRL_T_OPTS="--preview 'bat --color=always --line-range=:50 {}'"
# Use fzf in a pipeline
cat urls.txt | fzf --multi | xargs -I{} curl -sO {}
# Git branch switcher
alias gb='git branch | fzf | xargs git checkout'
# Kill process interactively
alias fk='ps aux | fzf | awk "{print \$2}" | xargs kill'
ripgrep (rg)
ripgrep is a blazing-fast search tool that respects .gitignore rules and supports full regex. It replaces grep -r for most use cases and is significantly faster on large codebases.
# Basic search
rg "TODO" src/
# Case-insensitive search
rg -i "error" logs/
# Search specific file types
rg --type py "import requests"
# Show context (2 lines before and after)
rg -C 2 "panic" main.go
# Count matches per file
rg -c "console.log" --type js
# Search for a fixed string (no regex)
rg -F "user.email" app/
# Search hidden files too
rg --hidden "API_KEY" .
# List files that would be searched
rg --files src/
Dotfile Management with Git and Symlinks
Managing your configuration files (dotfiles) with Git ensures you can reproduce your setup on any machine in minutes.
# One-time setup
mkdir -p ~/dotfiles
cd ~/dotfiles
git init
# Move existing configs in and symlink
mv ~/.bashrc ~/dotfiles/bashrc
ln -s ~/dotfiles/bashrc ~/.bashrc
mv ~/.tmux.conf ~/dotfiles/tmux.conf
ln -s ~/dotfiles/tmux.conf ~/.tmux.conf
# Track and push
git add -A
git commit -m "Initial dotfiles"
git remote add origin [email protected]:you/dotfiles.git
git push -u origin main
For more sophisticated setups, consider GNU Stow, which automates symlink creation from a directory tree. A typical Stow layout puts each program's configs in its own directory, and stow bash creates the correct symlinks automatically.
direnv: Per-Project Environment Variables
direnv automatically loads and unloads environment variables when you enter or leave a directory. This is ideal for setting project-specific secrets, paths, or tool versions without polluting your global shell environment.
# Install
# brew install direnv (macOS)
# Add the hook to your shell
# For bash, add to .bashrc:
eval "$(direnv hook bash)"
# Create a .envrc in your project directory
cd ~/projects/myapp
echo 'export DATABASE_URL="postgres://localhost/myapp_dev"' > .envrc
echo 'export AWS_PROFILE="myapp"' >> .envrc
# Allow the file (required for security)
direnv allow
When you cd into ~/projects/myapp, the variables are loaded. When you leave, they are unloaded. The .envrc file should be added to .gitignore if it contains secrets.
Starship Prompt
Starship is a fast, customizable, cross-shell prompt written in Rust. It shows relevant context (git branch, language versions, AWS profile, Kubernetes context) without any configuration out of the box.
# Install
# brew install starship (macOS)
# curl -sS https://starship.rs/install.sh | sh (Linux)
# Add to the end of ~/.bashrc
eval "$(starship init bash)"
# Or for Zsh, add to ~/.zshrc
eval "$(starship init zsh)"
Customize with ~/.config/starship.toml:
[character]
success_symbol = "[>](bold green)"
error_symbol = "[>](bold red)"
[git_branch]
format = "[$branch]($style) "
[python]
format = "[py $version]($style) "
[aws]
format = "[$profile]($style) "
[nodejs]
format = "[node $version]($style) "
Next Steps
For an alternative shell experience, explore Zsh and Fish. To brush up on core scripting concepts that underpin many of these tools, see Bash Fundamentals. Return to the Shell Scripting hub for all available topics.