Shell Customization#

Concepts#

Shell Startup Files#

When Bash starts, it reads configuration files — but which files depend on how the shell was started.

Login vs Non-Login Shell#

Type When Example
Login shell First shell after authentication SSH session, TTY login, su - user
Non-login shell Shells opened inside an existing session Opening a terminal in the desktop, running bash

Interactive vs Non-Interactive Shell#

Type When Example
Interactive You type commands at a prompt Terminal window, SSH session
Non-interactive Runs a script, no prompt bash script.sh, cron jobs

Which Files Are Read?#

Login shell (interactive):
  /etc/profile → ~/.bash_profile OR ~/.bash_login OR ~/.profile (first found)

Non-login shell (interactive):
  /etc/bash.bashrc → ~/.bashrc

Non-interactive shell (scripts):
  $BASH_ENV (if set)

In practice, on Ubuntu and Debian, ~/.profile sources ~/.bashrc:

# Default ~/.profile on Ubuntu/Debian contains:
if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

This means .bashrc runs in almost every interactive session. Put your customizations there.

Summary#

File What to put in it
~/.bashrc Aliases, functions, prompt, shell options — anything interactive
~/.profile Environment variables (PATH, EDITOR, LANG) — login-time settings
~/.bash_profile Rarely needed on Ubuntu/Debian (.profile is used instead)
/etc/profile System-wide login settings (don’t edit unless you’re root)
/etc/bash.bashrc System-wide interactive shell settings

Environment Variables#

Environment variables are key-value pairs available to all processes. Child processes inherit them from their parent.

# View all environment variables
env
printenv

# View a specific variable
echo $HOME
echo $PATH
echo $SHELL

# Set a variable (current shell only)
MY_VAR="hello"

# Export it (available to child processes)
export MY_VAR="hello"

# Unset a variable
unset MY_VAR

# Common variables
echo $USER          # current username
echo $HOME          # home directory
echo $PWD           # current directory
echo $SHELL         # default shell
echo $EDITOR        # preferred text editor
echo $LANG          # language/locale
echo $TERM          # terminal type

To make a variable permanent, add the export line to ~/.profile (for login shells) or ~/.bashrc (for all interactive shells):

# In ~/.bashrc or ~/.profile
export EDITOR="vim"
export VISUAL="vim"

The PATH Variable#

PATH is a colon-separated list of directories the shell searches for commands:

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

# The shell searches left to right. First match wins.

Adding to PATH#

# Add a directory to the END of PATH
export PATH="$PATH:$HOME/bin"

# Add a directory to the BEGINNING of PATH (higher priority)
export PATH="$HOME/.local/bin:$PATH"

# Make it permanent — add to ~/.bashrc or ~/.profile
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc

Where Commands Live#

which python3       # show which python3 will run
type ls             # show if ls is alias, function, or binary
command -v git      # check if git exists in PATH

Aliases#

An alias is a shortcut for a command:

# Define an alias
alias ll='ls -alF'
alias la='ls -A'
alias update='sudo apt update && sudo apt upgrade -y'
alias gs='git status'
alias ..='cd ..'
alias ...='cd ../..'
alias grep='grep --color=auto'

# View all aliases
alias

# Remove an alias (current session only)
unalias ll

# Run the original command (bypass alias)
\ls          # backslash bypasses the alias
command ls   # also bypasses aliases

Make aliases permanent by adding them to ~/.bashrc:

# ~/.bashrc
alias ll='ls -alF'
alias update='sudo apt update && sudo apt upgrade -y'

Tip: Some users keep aliases in a separate file ~/.bash_aliases. Ubuntu’s default .bashrc already sources this file if it exists.

Shell Functions#

Functions are more powerful than aliases — they accept arguments and can contain logic:

# Define a function
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Use it
mkcd my-new-project    # creates dir and cd's into it

# More examples
extract() {
    case "$1" in
        *.tar.gz)  tar xzf "$1" ;;
        *.tar.bz2) tar xjf "$1" ;;
        *.tar.xz)  tar xJf "$1" ;;
        *.zip)     unzip "$1" ;;
        *.gz)      gunzip "$1" ;;
        *)         echo "Unknown format: $1" ;;
    esac
}

# Find and cd into a directory
fcd() {
    local dir
    dir=$(find . -type d -name "$1" 2>/dev/null | head -1)
    if [ -n "$dir" ]; then
        cd "$dir"
    else
        echo "Directory '$1' not found"
    fi
}

Add functions to ~/.bashrc to make them permanent.

Customizing the Prompt (PS1)#

The PS1 variable controls your command prompt. Special escape sequences:

Escape Meaning
\u Username
\h Hostname (short)
\H Hostname (full)
\w Current directory (full path, ~ for home)
\W Current directory (basename only)
\d Date
\t Time (24h)
\T Time (12h)
\n Newline
\$ $ for regular user, # for root
\[...\] Non-printing characters (for colors — prevents line-wrapping issues)

Examples#

# Simple
PS1='\u@\h:\w\$ '
# kmiguel@mypc:~/projects$

# Minimal
PS1='\W\$ '
# projects$

# With git branch (if git is installed)
PS1='\u@\h:\w$(__git_ps1 " (%s)")\$ '
# kmiguel@mypc:~/project (main)$

Adding Colors#

# Color codes (wrap in \[ and \] to prevent display issues)
# \[\e[COLORm\]  — start color
# \[\e[0m\]      — reset to default

# Common colors: 31=red, 32=green, 33=yellow, 34=blue, 35=magenta, 36=cyan
# Bold: add 1; before the color number

# Green user@host, blue directory
PS1='\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]\$ '

Shell Options#

# Useful options to add to ~/.bashrc

# Append to history instead of overwriting
shopt -s histappend

# Resize terminal output on window resize
shopt -s checkwinsize

# cd into directories by typing just the name
shopt -s autocd

# Correct minor typos in cd
shopt -s cdspell

# Extended globbing (pattern matching)
shopt -s extglob

# History settings
HISTSIZE=10000            # lines in memory
HISTFILESIZE=20000        # lines in file
HISTCONTROL=ignoreboth    # ignore duplicates and lines starting with space

Applying Changes#

After editing ~/.bashrc:

source ~/.bashrc
# or
. ~/.bashrc

Lab#

Exercise 1: Explore Your Startup Files#

# What shell are you running?
echo $SHELL
echo $0

# View your current .bashrc
cat ~/.bashrc

# Check if .profile sources .bashrc
grep -n bashrc ~/.profile

# View your PATH
echo $PATH | tr ':' '\n'

Exercise 2: Create Aliases#

# Add aliases to ~/.bashrc
cat >> ~/.bashrc << 'EOF'

# Custom aliases
alias ll='ls -alF'
alias la='ls -A'
alias ..='cd ..'
alias ...='cd ../..'
alias h='history | tail -20'
alias ports='ss -tlnp'
EOF

# Apply
source ~/.bashrc

# Test
ll
..
h

Exercise 3: Add a Function#

# Add to ~/.bashrc
cat >> ~/.bashrc << 'EOF'

# Create directory and cd into it
mkcd() {
    mkdir -p "$1" && cd "$1"
}
EOF

source ~/.bashrc

# Test
mkcd /tmp/testdir
pwd
# /tmp/testdir
cd ~
rmdir /tmp/testdir

Exercise 4: Customize Your Prompt#

# Save current PS1
echo "Current PS1: $PS1"

# Try a colored prompt
PS1='\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]\$ '

# If you like it, add to ~/.bashrc:
# (replace the existing PS1 line or add at the end)

Exercise 5: Manage PATH#

# Create a personal bin directory
mkdir -p ~/bin

# Add it to PATH in .bashrc
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# Create a simple script in it
cat > ~/bin/greet << 'EOF'
#!/bin/bash
echo "Hello, $USER! Today is $(date '+%A, %B %d')."
EOF
chmod +x ~/bin/greet

# Run it from anywhere
greet

Review#

1. What is the difference between ~/.bashrc and ~/.profile?

~/.bashrc runs for every interactive non-login shell (and is typically sourced by ~/.profile on Ubuntu/Debian). Put aliases, functions, and prompt customization here. ~/.profile runs only for login shells. Put environment variables (export) here.

2. What is the PATH variable?

A colon-separated list of directories the shell searches (left to right) when you type a command. The first matching executable found wins.

3. How do you make an alias permanent?

Add the alias line to ~/.bashrc (or ~/.bash_aliases on Ubuntu). Then run source ~/.bashrc to apply it in the current session.

4. How do you apply changes to .bashrc without logging out?

Run source ~/.bashrc (or . ~/.bashrc). This re-reads the file in the current shell.

5. What does PS1 control?

The primary command prompt. It supports escape sequences like \u (username), \h (hostname), \w (working directory), and color codes.


Previous: tmux | Next: find, locate, and xargs