.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 fileHISTCONTROL=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
.bashrcmight 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.bashrcwill breakscp,rsync, and any tool that expects a clean shell. Save greetings for.bash_profileonly. - Slow commands. Everything in
.bashrcruns 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.