You add a handy alias to your setup, open a new terminal, and it works perfectly. Then a cron job that uses the same alias fails, or an ssh server mycommand run comes back with command not found, and the setup that clearly works in front of you seems to evaporate the moment something else runs it. Nothing is broken. The shell simply reads different startup files depending on how it was launched, and once you know which file runs when, the mystery disappears and you can shape the shell to fit your hands.
This last technical part is about that shaping: the files that configure your shell, the difference between them that trips everyone, and the aliases, variables, and prompt tweaks that turn a bare shell into your own.
New to this? Make one small change to ~/.bashrc, reload it, and see the effect before adding more. Been editing dotfiles for years? Skip to the login versus non-login section, because it is the reason your PATH behaves differently in cron and scripts. This closes the loop on the shell from Part 3 and the cron environment from Part 15.
A login shell, such as an SSH session or a console login, reads /etc/profile and then your ~/.bash_profile or ~/.profile. An interactive non-login shell, such as a new terminal tab, reads ~/.bashrc. A non-interactive shell, such as a script or a cron job, reads neither by default. Put per-session settings like aliases and the prompt in ~/.bashrc, have ~/.bash_profile source ~/.bashrc so both paths agree, and never rely on either for a script, where you set what you need explicitly.
Login, non-login, and why it matters
Bash decides which files to read based on how it started. A login shell is what you get when you sign in fresh: an SSH connection, a text console, or su - with the dash. It reads the profile files, meant for things you set once per session. An interactive shell that is not a login, like opening another tab in a terminal that is already signed in, skips the profile files and reads ~/.bashrc instead, which is why that file is where day to day settings belong.
The third case is the one that surprises people. A non-interactive shell, the kind that runs a script or a cron job, reads none of these by default. That is deliberate, so scripts behave the same for everyone regardless of personal setup, but it is exactly why an alias or a PATH entry that works in your terminal is missing when cron runs the same command.
| How it started | Kind | Reads |
|---|---|---|
SSH, console, su - | Login | /etc/profile, ~/.bash_profile |
| New terminal tab | Interactive non-login | ~/.bashrc |
| A shell script | Non-interactive | nothing by default |
| A cron job | Non-interactive | nothing, short PATH |
Because of this split, nearly every distribution ships a ~/.bash_profile that sources ~/.bashrc, so a login shell picks up your interactive settings too. If you keep everyday settings in ~/.bashrc and let the profile pull it in, both a fresh SSH login and a new tab end up the same. That one line is the fix for most of the confusion.
You add alias deploy='/opt/app/deploy.sh' to ~/.bashrc, it works all day in your terminal, then a cron job calling deploy fails with command not found. The reason is now clear: cron runs a non-interactive shell that never reads ~/.bashrc, so the alias does not exist there. Aliases in particular are ignored in scripts even if the file were read.
The fix is not to force the file to load; it is to stop depending on interactive settings for automated work. In scripts and cron, call the full path /opt/app/deploy.sh directly, and set any PATH you need at the top of the script or the crontab. Keep aliases for your fingers, and full paths for the machine.
Aliases, functions, and variables
With the right file chosen, the fun part is filling it. An alias is a short name for a longer command. A function does the same for anything that needs arguments or several steps. An exported variable passes a setting to the programs you run, such as your preferred editor. These three lines cover most of what people put in a ~/.bashrc.
# in ~/.bashrc
alias ll='ls -alh'
alias gs='git status'
export EDITOR=vim
export PATH="$HOME/bin:$PATH" # your own scripts win over system ones
# a function, for when an alias is not enough
mkcd() { mkdir -p "$1" && cd "$1"; }
$ source ~/.bashrc # apply changes without opening a new shell
The PATH line shows an important detail. The shell searches PATH from left to right and runs the first match, so putting $HOME/bin at the front means your own tools take priority, while putting it at the end means system tools win. After editing the file, run source ~/.bashrc to apply it to the current shell rather than opening a new one.
A prompt and history that help
Two more settings earn their keep every day. The prompt, held in the variable PS1, can show your user, host, and current directory, which stops you running the right command on the wrong machine. And a few history settings make the up arrow and search far more useful by keeping more lines and dropping duplicates.
# a clear prompt: user@host in the current directory
export PS1='u@h:w$ '
# better history, also in ~/.bashrc
export HISTSIZE=10000
export HISTCONTROL=ignoredups:erasedups # drop repeated commands
shopt -s histappend # keep history across sessions
The u, h, and w in the prompt stand for the user, host, and working directory. A prompt that names the host is a quiet safety feature, because the moment a production hostname stares back at you, you slow down before typing something destructive.
| Add to ~/.bashrc | Effect |
|---|---|
alias ll='ls -alh' | A short name for a long command |
export EDITOR=vim | Tools open your preferred editor |
export PATH=... | Add your own tools to the search |
PS1='...' | Shape the prompt, show the host |
HISTCONTROL=... | Cleaner, longer command history |
Shell variables versus the environment
One distinction underpins everything you just set. A plain assignment like x=5 creates a shell variable that lives only in the current shell. Putting export in front of it promotes it to an environment variable, which means the programs you launch inherit it. That is the whole reason export matters for settings like EDITOR and PATH: without it, the value stays with your shell and never reaches the tools that need it.
$ FOO=bar # shell variable, this shell only
$ bash -c 'echo $FOO' # a child process sees nothing
$ export FOO=bar # now it is in the environment
$ bash -c 'echo $FOO' # the child inherits it
bar
$ printenv HOME # inspect one environment variable
/home/priya
To see what is set, printenv or env lists the environment, while set shows every shell variable including the ones not exported. For a simple value that every user and login should have, such as a system wide language or a shared PATH entry, /etc/environment is the clean home. It holds plain KEY=value lines with no scripting, read once at login, which makes it safer and more predictable than editing a profile script. Keep application secrets out of it, though, since environment variables are visible to the processes that inherit them and to anyone who can read the process list.
A real opinion: keep dotfiles in git, keep them lean
Two habits separate a shell setup that helps from one that hurts. First, keep your dotfiles in a git repository. The day you get a new laptop or a fresh server account, you clone the repo and your whole environment is back in a minute instead of being rebuilt from memory over weeks. Your ~/.bashrc, aliases, and prompt are real configuration, and configuration belongs in version control like any other.
Second, keep ~/.bashrc lean, because it runs for every interactive shell you open. Loading a slow tool or a heavy prompt framework there adds a visible pause to every new tab. I also part ways with the popular alias rm='rm -i'. It feels safe, but it trains you to expect a prompt that will not be there on the next machine, so you fire off a careless rm and lose files precisely because the guard you leaned on was missing. Build the habit of thinking before you delete, not the habit of relying on a prompt.
Make three small changes, reload, and feel the difference. Add these to the end of ~/.bashrc:
echo "alias ll='ls -alh'" >> ~/.bashrc
echo 'export HISTCONTROL=ignoredups' >> ~/.bashrc
source ~/.bashrc
ll
Open a new terminal tab and run ll again to confirm it persists. Then, if it is not already there, check that ~/.bash_profile sources ~/.bashrc so an SSH login gets the same setup.
What is the difference between .bashrc and .bash_profile, and why does it matter?
~/.bash_profile runs for a login shell, such as an SSH session, while ~/.bashrc runs for an interactive non-login shell, such as a new terminal tab. It matters because settings placed in the wrong one appear to work sometimes and not others. The clean answer names the convention of having ~/.bash_profile source ~/.bashrc so both agree, and adds that non-interactive shells like cron read neither, which is why automated jobs need explicit paths. That last point signals you have actually debugged a cron PATH problem.
Dotfile questions
I changed .bashrc but nothing happened. Why? An open shell does not reread the file automatically. Run source ~/.bashrc to apply it now, or open a new terminal. If the change still does not show, confirm it is in the file that your shell type actually reads.
Where should a PATH change go? For interactive use, ~/.bashrc is fine. For something every login and service should see, a login profile or a file in /etc/profile.d is better. For a script or cron job, set PATH inside the job, because those read neither.
What is the difference between an alias and a function? An alias is a plain text shortcut with no arguments of its own. A function can take arguments, run several commands, and use logic. Reach for a function the moment you need to pass a value or do more than one thing.
Should I edit /etc/profile for a system-wide change? Prefer dropping a file into /etc/profile.d instead of editing /etc/profile directly, so your change survives package updates and is easy to remove. The same idea as a drop-in applies here.
Do zsh or fish use the same files? No, each shell has its own startup files, like ~/.zshrc for zsh. The login versus non-login idea carries over, but the filenames differ, so setup does not transfer directly between shells.
Your shell now fits you, and you understand why it behaves one way in a terminal and another in cron. That completes the technical arc of this series, from the filesystem all the way to a tuned environment. One part remains, and it is the one that turns all of this into a career: which certifications are worth your money, what interviews actually test, and how to land the first role. Work the series in order from the Complete Guide.


DrJha