.bashrc shell

your shell starts stupid. this file makes it smart.

Every time you open a terminal, bash reads a file called ~/.bashrc. Every time. Without fail. It runs whatever is in that file before you see a prompt.

You have been opening terminals for years. You have been manually typing the same setup commands, setting the same environment variables, adjusting the same settings — every session. Or worse, you haven’t been doing any of that because you didn’t know you could. You just accepted whatever defaults your distro gave you and called it a day.

Your .bashrc is where you teach your shell how to behave. Aliases, environment variables, functions, prompt customization, PATH modifications — it all goes here. Without it, every terminal session starts from zero. With it, every terminal session starts exactly where you want it.

Unless you’re running Windows then wtf none of this applies to you. But hey, come to the dark side, go install WSL2 and you can follow along. We’ll wait. Impatiently.

If you’re lazy like me (all sysadmins are!) then click here for the .bashrc cheat sheet.


Where is it and how does it work?

~/.bashrc

That’s ~ (your home directory) followed by .bashrc (a hidden file — the dot makes it hidden). It’s a plain text file. It’s just a bash script that runs automatically when you open an interactive, non-login shell. Which is every time you open a new terminal window or tab.

Edit it

nano ~/.bashrc

Or vim, or whatever editor you use. It’s a text file. Edit it. Save it.

Reload it without closing your terminal

source ~/.bashrc

Or the shorter version:

. ~/.bashrc

Changes take effect immediately. No need to close and reopen your terminal like it’s a Windows application.


.bashrc vs .bash_profile — the question everyone asks

Short answer: .bashrc runs for interactive non-login shells (opening a terminal). .bash_profile runs for login shells (SSH sessions, console login, first terminal on some systems).

The pragmatic answer: put everything in .bashrc and add this to your .bash_profile:

if [ -f ~/.bashrc ]; then
    source ~/.bashrc
fi

Now .bash_profile loads .bashrc, and everything works everywhere. Stop worrying about which file to edit. This has been the correct answer since approximately 1997.


Aliases: typing less, doing more

An alias is a shortcut. You type a short word, bash expands it into a longer command.

Essential aliases

# ls upgrades — stop using bare ls
alias ll='ls -lah'
alias la='ls -A'
alias lt='ls -laht'

# safety nets — ask before destroying things
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

# navigation shortcuts
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'

# grep with color (usually default, but be sure)
alias grep='grep --color=auto'

ll is the alias you’ll use most. ls -lah shows everything — permissions, sizes in human-readable format, hidden files. You’ll type ll reflexively within a week.

The rm -i alias asks for confirmation before deleting. You’ll curse it when deleting ten files. You’ll thank it when you almost delete the wrong one.

Aliases for things you type constantly

# git shortcuts
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline -20'

# docker shortcuts
alias dps='docker ps'
alias dimg='docker images'
alias dex='docker exec -it'

# system info
alias ports='ss -tuln'
alias myip='curl -s ifconfig.me'
alias diskspace='df -h -x tmpfs -x devtmpfs -x squashfs'

Every alias saves you a few keystrokes. Multiply that by “every day, forever” and it adds up to a non-trivial amount of your life that you got back.

Check what an alias does

alias ll

Shows what ll expands to. Useful when you’ve forgotten what past-you was thinking.

Temporarily bypass an alias

\rm file.txt

The backslash runs the real rm without your -i alias. For when you know what you’re doing and don’t need a safety net.


Environment variables: configure once, use everywhere

Environment variables are settings that programs read. Instead of passing flags every time, you set a variable once and programs pick it up automatically.

Setting variables

export EDITOR=vim
export VISUAL=vim

Now every program that needs an editor (git commit, crontab -e, etc.) uses vim. Or nano. Or whatever you prefer. Set it once, never think about it again.

export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoreboth
  • HISTSIZE — number of commands to remember in memory (default is usually 500 or 1000)
  • HISTFILESIZE — number of commands to save to the history file
  • HISTCONTROL=ignoreboth — don’t save duplicate commands or commands that start with a space. The space trick is useful when you’re typing passwords or sensitive commands you don’t want in your history.
export HISTTIMEFORMAT="%F %T  "

Adds timestamps to your history output. Now you know when you ran that command, not just that you ran it sometime between now and the heat death of the universe.


Functions: when aliases aren’t enough

Aliases can’t take arguments in the middle or do anything complex. Functions can.

Make a directory and cd into it

mkcd() {
    mkdir -p "$1" && cd "$1"
}

mkcd projects/new-thing creates the directory and puts you in it. One command instead of two. The -p flag creates parent directories if they don’t exist.

Extract anything

extract() {
    case "$1" in
        *.tar.gz|*.tgz)  tar -xzf "$1" ;;
        *.tar.bz2)       tar -xjf "$1" ;;
        *.tar.xz)        tar -xJf "$1" ;;
        *.tar)           tar -xf "$1" ;;
        *.zip)           unzip "$1" ;;
        *.gz)            gunzip "$1" ;;
        *.7z)            7z x "$1" ;;
        *)               echo "Unknown format: $1" ;;
    esac
}

extract archive.tar.gz or extract file.zip or extract package.7z — one command handles everything. You don’t need to remember whether it’s -xzf or -xjf or unzip. The function remembers for you. (Though you should still know — the tar page has you covered.)

Quick HTTP server

serve() {
    python3 -m http.server "${1:-8000}"
}

serve starts a web server in the current directory on port 8000. serve 3000 uses port 3000. For when you need to share files or test something quickly without installing anything.


PATH: add it here, not everywhere

Your PATH is the list of directories bash searches when you type a command. If you install something to a non-standard location, add it to PATH in your .bashrc:

export PATH="$HOME/.local/bin:$PATH"
export PATH="$HOME/go/bin:$PATH"

The $HOME/.local/bin line is especially common — it’s where pip install --user and other tools put their executables. If you’ve ever installed something and then got “command not found,” this is probably why.

Always prepend (put your path first) rather than append. Prepending means your local versions take priority over system versions.


Organizing your .bashrc

As your .bashrc grows, keep it organized. Comments and sections help future-you find things.

# ─── Aliases ──────────────────────────────────
alias ll='ls -lah'
# ... more aliases ...

# ─── Environment Variables ────────────────────
export EDITOR=vim
# ... more exports ...

# ─── Functions ────────────────────────────────
mkcd() { mkdir -p "$1" && cd "$1"; }
# ... more functions ...

# ─── PATH ────────────────────────────────────
export PATH="$HOME/.local/bin:$PATH"
# ... more PATH entries ...

Or split it into separate files and source them:

[ -f ~/.bash_aliases ] && source ~/.bash_aliases
[ -f ~/.bash_functions ] && source ~/.bash_functions

That [ -f file ] && source file pattern checks if the file exists before sourcing it. No errors if you haven’t created the file yet.


Things to NOT put in your .bashrc

  • Passwords, API keys, or tokens. Your .bashrc might end up in a dotfiles repo. Use a separate file that’s gitignored, or use a secrets manager.
  • Commands that produce output. An echo "Welcome!" in your .bashrc will break scp, rsync, and any tool that expects a clean shell. Save greetings for .bash_profile only.
  • Slow commands. Everything in .bashrc runs before you see a prompt. If you put a 2-second API call in there, every terminal takes 2 seconds to open. Don’t do that.

The flags that actually matter

This isn’t about flags — it’s about what goes in the file.

What to add Why
alias ll='ls -lah' The ls upgrade you’ll use daily.
alias rm='rm -i' Ask before deleting. Saved many a career.
export EDITOR=vim Set your default editor once.
export HISTSIZE=10000 Remember more command history.
export HISTCONTROL=ignoreboth Don’t save duplicates or space-prefixed commands.
export PATH="$HOME/.local/bin:$PATH" Find locally installed tools.
mkcd() { mkdir -p "$1" && cd "$1"; } Create and enter a directory in one shot.

“But I just—”

No.

“I just set things up manually each time.” You open a terminal. You type export EDITOR=vim. You type your aliases. You set your PATH. Then your terminal crashes, or you open a new tab, and you do it all again. That’s not a workflow. That’s a punishment.

“My distro’s default .bashrc is fine.” Your distro’s default .bashrc has some ls color support and a comment block telling you to put aliases in ~/.bash_aliases. It’s a skeleton. It’s the IKEA instructions without the furniture. You need to actually build something.

“I use Oh My Zsh so I don’t need to configure things.” Oh My Zsh is 1,400 files of someone else’s configuration that you don’t understand. When it breaks — and it will — you won’t know why because you never learned how your shell works. A 20-line .bashrc you wrote yourself will outperform a framework you can’t debug.

“I’ll configure it later.” You said that six months ago. You’re still pressing Up forty times to find that rsync command. You’re still typing cd ../../../ instead of .... Future you is present you, just more annoyed.


.bashrc cheat sheet

You made it. Or you skipped straight here. Either way, no judgment. Copy and paste these. Pin them. Tattoo them on your forearm. Whatever works.

What you’re doing What to add to ~/.bashrc
Better ls alias ll='ls -lah'
Sort by time alias lt='ls -laht'
Safe delete alias rm='rm -i'
Quick navigation alias ..='cd ..'
Git shortcuts alias gs='git status'
Set default editor export EDITOR=vim
Bigger history export HISTSIZE=10000
History timestamps export HISTTIMEFORMAT="%F %T "
No duplicate history export HISTCONTROL=ignoreboth
Add to PATH export PATH="$HOME/.local/bin:$PATH"
mkdir + cd mkcd() { mkdir -p "$1" && cd "$1"; }
Reload after changes source ~/.bashrc

The one thing: source ~/.bashrc — reload your config without closing the terminal. Edit, source, test. Repeat.

Back to the top, you overachiever.