Introduction
Congratulations on reaching the FINAL post in the LFCS Phase 1 series! In this comprehensive guide, we'll explore one of the most critical aspects of mastering Linux: understanding and configuring bash startup files.
Every time you log in to a Linux system or open a terminal, bash executes a series of configuration files that set up your environment. These files control everything from your command prompt appearance to your PATH variable, aliases, shell functions, and environment variables. Understanding these files is essential for customizing your shell environment and troubleshooting configuration issues.
In this tutorial, you'll learn:
- The difference between login shells and non-login shells (critical concept!)
- System-wide configuration files:
/etc/environment,/etc/profile,/etc/bashrc - User-specific configuration files:
~/.bash_profile,~/.bashrc,~/.bash_logout - The order of execution for startup files
- When to modify which configuration file
- How to reload configuration with the
sourcecommand - Creating custom scripts in
/etc/profile.d/ - Best practices for system-wide vs per-user configuration
- Real-world configuration examples and patterns
By the end of this guide, you'll have complete mastery of bash configuration files and be able to customize your shell environment like a pro!
What Are Bash Startup Files?
Bash startup files (also called configuration files or initialization files) are scripts that bash executes automatically when you start a new shell session. These files contain commands that:
- Set environment variables (PATH, EDITOR, etc.)
- Define aliases and shell functions
- Configure the command prompt (PS1)
- Set shell options (history size, completion behavior)
- Execute custom commands at login or logout
Think of startup files as your shell's "boot sequence" - they prepare your working environment before you start entering commands.
Why Multiple Configuration Files?
Linux uses multiple configuration files for flexibility:
- System-wide vs user-specific: Admins can set defaults for all users, while individual users can customize their own environments
- Login vs non-login shells: Different types of shell sessions need different initialization
- Distribution compatibility: Different distributions use different file locations
- Separation of concerns: Keep login-specific settings separate from interactive shell settings
Login Shells vs Non-Login Shells
The most critical concept in understanding bash startup files is the difference between login shells and non-login shells. Different files are executed depending on which type of shell you're starting.
What Is a Login Shell?
A login shell is the first shell you get when you log in to a system. It's called a "login" shell because it requires authentication.
Examples of login shells:
- SSH into a remote server:
ssh user@server - Log in at a text console (Ctrl+Alt+F2 on most systems)
- Switch users with
su - username(note the dash!) - Start bash explicitly as a login shell:
bash --loginorbash -l
Login shells execute a specific set of configuration files designed for one-time login initialization.
What Is a Non-Login Shell?
A non-login shell is any shell you start that doesn't require authentication. You're already logged in, and you're just opening another shell.
Examples of non-login shells:
- Opening a new terminal window in your desktop environment
- Running a bash script
- Opening a new tab in your terminal emulator
- Starting a subshell with just
bash(no login flag) - Switch users with
su username(no dash)
Non-login shells execute different configuration files, typically just ~/.bashrc.
How to Tell Which Type You're In
You can check if your current shell is a login shell:
# Method 1: Check $0 (shell name)
echo $0
# Output for login shell:
-bash # Notice the leading dash!
# Output for non-login shell:
bash # No leading dash
The leading dash (-bash) indicates a login shell.
# Method 2: Use shopt command
shopt login_shell
# Output for login shell:
login_shell on
# Output for non-login shell:
login_shell off
Overview of Configuration Files
Here's a complete overview of bash configuration files and when they're executed:
| File Path | Scope | Executed When | Purpose |
|---|---|---|---|
/etc/environment | System-wide | All shells (not bash-specific) | Environment variables only |
/etc/profile | System-wide | Login shells | Login initialization for all users |
/etc/profile.d/*.sh | System-wide | Login shells (sourced by /etc/profile) | Modular system-wide login scripts |
/etc/bashrc or /etc/bash.bashrc | System-wide | Non-login interactive shells | Non-login initialization for all users |
~/.bash_profile | User-specific | Login shells (first found) | User's login initialization |
~/.bash_login | User-specific | Login shells (if .bash_profile missing) | Alternative login file |
~/.profile | User-specific | Login shells (if both above missing) | POSIX-compatible login file |
~/.bashrc | User-specific | Non-login interactive shells | User's interactive shell settings |
~/.bash_logout | User-specific | When login shell exits | Cleanup tasks on logout |
Execution Order for Login Shells
When you start a login shell, bash executes files in this specific order:
1. /etc/environment (if it exists - not bash-specific)
2. /etc/profile (system-wide login initialization)
├─> /etc/profile.d/*.sh (custom scripts sourced by /etc/profile)
3. First file found in user's home directory:
├─> ~/.bash_profile (checked first)
└─> ~/.bash_login (if .bash_profile not found)
└─> ~/.profile (if neither above found)
4. ~/.bash_logout (executed when login shell exits)
Important notes:
- Only one of
~/.bash_profile,~/.bash_login, or~/.profileis executed (the first one found) - Most modern systems use
~/.bash_profile - Many
.bash_profilefiles source~/.bashrcto ensure interactive settings are also loaded
Execution Order for Non-Login Shells
When you start a non-login interactive shell (like opening a terminal window), bash executes:
1. /etc/bashrc or /etc/bash.bashrc (system-wide, if it exists)
2. ~/.bashrc (user-specific)
That's it! Non-login shells only read these two files (if they exist).
Note: The exact location varies by distribution:
- RHEL/CentOS/Fedora:
/etc/bashrc - Debian/Ubuntu:
/etc/bash.bashrc
System-Wide Configuration Files
Let's explore the system-wide configuration files that affect all users on the system.
/etc/environment
Purpose: Set environment variables for all users and all shells (not just bash).
Executed: At login, by the PAM (Pluggable Authentication Modules) system.
Format: Simple KEY=value format (NOT a shell script).
# View the file
cat /etc/environment
# Typical content:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
LANG="en_US.UTF-8"
When to edit:
- Setting system-wide environment variables
- Modifying the default PATH for all users
- Setting locale (LANG, LC_ALL)
Important: /etc/environment is NOT a shell script! Don't use:
exportcommands- Variable substitution (
$HOME, etc.) - Conditional statements
Only use simple KEY=value assignments.
/etc/profile
Purpose: System-wide login initialization for bash and compatible shells.
Executed: When any user starts a login shell.
Format: Shell script (you can use full bash syntax).
# View the file
cat /etc/profile
# Typical content (excerpt):
# /etc/profile
# System-wide environment and startup programs
# Functions and aliases go in /etc/bashrc
pathmunge () {
case ":${PATH}:" in
*:"$1":*)
;;
*)
if [ "$2" = "after" ] ; then
PATH=$PATH:$1
else
PATH=$1:$PATH
fi
esac
}
# Add /usr/local/bin to PATH
pathmunge /usr/local/bin
# Source scripts in /etc/profile.d/
for i in /etc/profile.d/*.sh ; do
if [ -r "$i" ]; then
. "$i"
fi
done
unset pathmunge
When to edit:
- Setting system-wide environment variables for login shells
- Executing commands that should run once at login (not for every shell)
- Typically you DON'T edit this directly - use
/etc/profile.d/instead
/etc/profile.d/
Purpose: Modular location for system-wide login initialization scripts.
Executed: Scripts here are sourced by /etc/profile during login.
Benefits:
- Modular: Each application can have its own file
- Clean: No need to edit
/etc/profiledirectly - Package-friendly: Software packages can drop scripts here during installation
# List scripts in /etc/profile.d/
ls /etc/profile.d/
# Typical output:
colorgrep.sh colorls.sh lang.sh less.sh vim.sh which2.sh
# Example: /etc/profile.d/custom-path.sh
# Add /opt/myapp/bin to PATH for all users
export PATH="/opt/myapp/bin:$PATH"
When to create files here:
- Adding custom paths to PATH system-wide
- Setting environment variables for specific applications
- Running initialization commands for all users at login
Naming convention: Files must end with .sh to be sourced by /etc/profile.
/etc/bashrc (RHEL/CentOS) or /etc/bash.bashrc (Debian/Ubuntu)
Purpose: System-wide initialization for non-login interactive shells.
Executed: When any user starts a non-login interactive bash shell.
Format: Shell script.
# On RHEL/CentOS/Fedora
cat /etc/bashrc
# On Debian/Ubuntu
cat /etc/bash.bashrc
# Typical content (excerpt):
# /etc/bashrc
# System-wide functions and aliases
# Environment stuff goes in /etc/profile
# By default, we want umask to get set.
if [ $UID -gt 199 ] && [ "`id -gn`" = "`id -un`" ]; then
umask 002
else
umask 022
fi
# Set a nice prompt
PS1="[\u@\h \W]\\$ "
When to edit:
- Setting aliases for all users
- Defining functions for all users
- Customizing the prompt (PS1) system-wide
- Settings that should apply to every interactive shell (not just login)
User-Specific Configuration Files
These files are in each user's home directory and allow personal customization.
~/.bash_profile
Purpose: User-specific login initialization.
Executed: When the user starts a login shell.
Common pattern: Most .bash_profile files source .bashrc to ensure interactive settings are loaded:
# View your .bash_profile
cat ~/.bash_profile
# Typical content:
# .bash_profile
# Source .bashrc if it exists
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User-specific environment and startup programs
export PATH="$HOME/bin:$PATH"
export EDITOR=vim
# Run any login-specific commands
echo "Welcome, $USER! You logged in at $(date)"
When to edit:
- Setting personal environment variables (EDITOR, PAGER, custom paths)
- Adding directories to your PATH
- Running commands that should execute once at login
- Sourcing
.bashrc(recommended pattern)
~/.bashrc
Purpose: User-specific interactive shell settings.
Executed: When the user starts a non-login interactive shell (and often sourced by .bash_profile for login shells too).
This is where most customization happens!
# View your .bashrc
cat ~/.bashrc
# Typical content:
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# User-specific aliases
alias ll='ls -lah --color=auto'
alias grep='grep --color=auto'
alias c='clear'
# User-specific functions
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Customize prompt
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
# History settings
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoredups:erasedups
# Custom PATH additions
export PATH="$HOME/.local/bin:$PATH"
# Application-specific settings
export VISUAL=vim
export EDITOR=vim
When to edit (most common use case):
- Defining personal aliases
- Creating shell functions
- Customizing your prompt (PS1)
- Setting history options
- Any settings for interactive shells
Best practice: Source the system-wide /etc/bashrc at the beginning (if it exists).
~/.bash_logout
Purpose: Cleanup tasks when exiting a login shell.
Executed: When a login shell exits (not executed for non-login shells).
# View your .bash_logout
cat ~/.bash_logout
# Typical content:
# ~/.bash_logout
# Clear the screen for privacy
clear
# Log logout time
echo "Logged out at $(date)" >> ~/logout.log
# Clean up temporary files
rm -f /tmp/myapp-$USER-*
# Save history explicitly
history -a
When to edit:
- Cleaning up temporary files
- Logging logout events
- Clearing sensitive information
- Running backup scripts on logout
Common Configuration Patterns
Let's look at practical examples of how to configure your bash environment.
Pattern 1: Adding a Directory to PATH
System-wide (all users) - create /etc/profile.d/custom-path.sh:
# /etc/profile.d/custom-path.sh
export PATH="/opt/myapp/bin:$PATH"
User-specific - edit ~/.bash_profile:
# Add to ~/.bash_profile
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
Pattern 2: Creating Useful Aliases
User-specific - edit ~/.bashrc:
# Add to ~/.bashrc
# Safety aliases (ask before overwriting)
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Enhanced ls commands
alias ll='ls -lah --color=auto'
alias la='ls -A'
alias l='ls -CF'
# Directory navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
# Git shortcuts
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline --graph'
# System shortcuts
alias c='clear'
alias h='history'
alias ports='netstat -tulanp'
alias myip='curl -s http://ipinfo.io/ip'
Pattern 3: Customizing Your Prompt
Edit ~/.bashrc:
# Simple: user@host:path$
export PS1='\u@\h:\w\$ '
# With colors: green user@host, blue path
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
# Show exit status of last command
export PS1='[\u@\h \W]$(if [ $? -eq 0 ]; then echo -e "\[\033[32m\]✓"; else echo -e "\[\033[31m\]✗"; fi)\[\033[00m\] \$ '
# Git-aware prompt (shows current branch)
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[33m\]$(parse_git_branch)\[\033[00m\]\$ '
Pattern 4: Setting Environment Variables
Login-only variables - edit ~/.bash_profile:
# Add to ~/.bash_profile
export EDITOR=vim
export VISUAL=vim
export PAGER=less
export BROWSER=firefox
Interactive shell variables - edit ~/.bashrc:
# Add to ~/.bashrc
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoreboth:erasedups
export HISTTIMEFORMAT="%F %T "
Pattern 5: Creating Shell Functions
Edit ~/.bashrc:
# Add to ~/.bashrc
# Create directory and cd into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Extract any archive type
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.gz) tar xzf "$1" ;;
*.tar.bz2) tar xjf "$1" ;;
*.zip) unzip "$1" ;;
*.gz) gunzip "$1" ;;
*) echo "Unknown archive format" ;;
esac
else
echo "File not found: $1"
fi
}
# Backup a file
backup() {
cp "$1" "$1.backup-$(date +%Y%m%d-%H%M%S)"
}
# Quick search in history
hs() {
history | grep "$1"
}
The source Command (Reloading Configuration)
After editing configuration files, you need to reload them for changes to take effect in your current shell.
What Is source?
The source command (also written as .) reads and executes commands from a file in the current shell (not a subshell).
Syntax:
source filename
# or
. filename
Reloading .bashrc
# After editing ~/.bashrc, reload it:
source ~/.bashrc
# Alternative syntax (POSIX-compatible):
. ~/.bashrc
This applies your changes immediately without logging out and back in.
Reloading .bash_profile
# After editing ~/.bash_profile:
source ~/.bash_profile
# Or:
. ~/.bash_profile
Why Not Just Run the File?
Running a script directly creates a subshell:
# This creates a subshell - variables won't affect current shell
./my_config.sh
bash my_config.sh
Using source executes commands in the current shell, so environment changes persist.
Example
# Edit .bashrc to add an alias
echo "alias hello='echo Hello World'" >> ~/.bashrc
# Before sourcing - command not found
hello
# Output: bash: hello: command not found
# Source the file
source ~/.bashrc
# Now it works
hello
# Output: Hello World
When to Modify Which File
Here's a decision guide for where to put your configuration:
| What You Want | File to Edit | Reason |
|---|---|---|
| Personal alias | ~/.bashrc | Aliases for interactive use |
| Personal function | ~/.bashrc | Functions for interactive use |
| Custom prompt (PS1) | ~/.bashrc | Prompt is for interactive shells |
| Personal environment variable | ~/.bash_profile | Set once at login, inherited by subshells |
| Add directory to PATH | ~/.bash_profile | PATH should be set at login |
| History settings | ~/.bashrc | History is for interactive use |
| Login greeting message | ~/.bash_profile | Should run once at login only |
| Cleanup at logout | ~/.bash_logout | Runs when login shell exits |
| Alias for all users | /etc/bashrc | System-wide interactive settings |
| PATH for all users | /etc/profile.d/custom.sh | Modular system-wide login config |
| Environment var for all shells | /etc/environment | Works for all shells, not just bash |
Real-World Configuration Examples
Let's put it all together with complete, real-world examples.
Example 1: Developer's .bashrc
# ~/.bashrc - Developer workstation configuration
# Source system-wide definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# ===== ALIASES =====
# Safety first - ask before overwriting
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Enhanced ls
alias ll='ls -lah --color=auto'
alias la='ls -A'
alias l='ls -CF'
# Directory navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
# Git shortcuts
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline --graph --all'
alias gd='git diff'
# Docker shortcuts
alias d='docker'
alias dc='docker-compose'
alias dps='docker ps'
alias dimg='docker images'
# System shortcuts
alias c='clear'
alias h='history'
alias ports='netstat -tulanp'
alias myip='curl -s http://ipinfo.io/ip'
# ===== FUNCTIONS =====
# Create directory and cd into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Extract any archive
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.gz) tar xzf "$1" ;;
*.tar.bz2) tar xjf "$1" ;;
*.tar.xz) tar xJf "$1" ;;
*.zip) unzip "$1" ;;
*.gz) gunzip "$1" ;;
*.bz2) bunzip2 "$1" ;;
*) echo "Unknown archive format" ;;
esac
else
echo "File not found: $1"
fi
}
# Quick backup
backup() {
cp "$1" "$1.backup-$(date +%Y%m%d-%H%M%S)"
echo "Backed up: $1"
}
# Search in history
hs() {
history | grep "$1"
}
# ===== PROMPT =====
# Git-aware prompt with colors
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[33m\]$(parse_git_branch)\[\033[00m\]\$ '
# ===== HISTORY =====
export HISTSIZE=50000
export HISTFILESIZE=100000
export HISTCONTROL=ignoreboth:erasedups
export HISTTIMEFORMAT="%F %T "
shopt -s histappend
# ===== OTHER SETTINGS =====
# Better tab completion
bind "set completion-ignore-case on"
bind "set show-all-if-ambiguous on"
# Check window size after each command
shopt -s checkwinsize
# Enable recursive globbing with **
shopt -s globstar 2>/dev/null
Example 2: Developer's .bash_profile
# ~/.bash_profile - Login shell initialization
# Source .bashrc for interactive settings
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# ===== ENVIRONMENT VARIABLES =====
# Default editors
export EDITOR=vim
export VISUAL=vim
export PAGER=less
# Development paths
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
export PATH="/usr/local/go/bin:$PATH"
export PATH="$HOME/go/bin:$PATH"
# Node.js and npm
export PATH="$HOME/.npm-global/bin:$PATH"
# Python
export PATH="$HOME/.local/bin:$PATH"
# Language settings
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
# Application-specific
export GOPATH="$HOME/go"
export PYTHONDONTWRITEBYTECODE=1
export NODE_ENV=development
# ===== STARTUP MESSAGE =====
echo "Welcome back, $USER!"
echo "Logged in at: $(date '+%Y-%m-%d %H:%M:%S')"
echo "Shell: $SHELL"
echo "-----"
Example 3: System Administrator's .bashrc
# ~/.bashrc - System Administrator configuration
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# ===== SAFETY ALIASES =====
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias chown='chown --preserve-root'
alias chmod='chmod --preserve-root'
alias chgrp='chgrp --preserve-root'
# ===== SYSTEM ADMIN ALIASES =====
# Service management
alias sstart='sudo systemctl start'
alias sstop='sudo systemctl stop'
alias srestart='sudo systemctl restart'
alias sstatus='sudo systemctl status'
alias senable='sudo systemctl enable'
alias sdisable='sudo systemctl disable'
# Log viewing
alias tailf='tail -f'
alias syslog='sudo tail -f /var/log/syslog'
alias messages='sudo tail -f /var/log/messages'
alias journal='sudo journalctl -f'
# Disk usage
alias df='df -h'
alias du='du -h'
alias free='free -h'
# Network
alias ports='netstat -tulanp'
alias listening='ss -tulpn'
alias ping='ping -c 5'
# Process management
alias psg='ps aux | grep -v grep | grep -i -e VSZ -e'
alias psmem='ps aux | sort -k 4 -r | head -10'
alias pscpu='ps aux | sort -k 3 -r | head -10'
# Package management (for RHEL/CentOS)
alias yi='sudo dnf install'
alias yr='sudo dnf remove'
alias ys='dnf search'
alias yu='sudo dnf update'
# ===== ADMIN FUNCTIONS =====
# Show most recent log entries
recent_logs() {
sudo find /var/log -type f -mtime -1 -exec ls -lh {} \; | sort -k 6,7
}
# Check if a service is running
is_running() {
systemctl is-active "$1" >/dev/null 2>&1 && echo "$1 is running" || echo "$1 is not running"
}
# Quick system info
sysinfo() {
echo "===== System Information ====="
echo "Hostname: $(hostname)"
echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"')"
echo "Kernel: $(uname -r)"
echo "Uptime: $(uptime -p)"
echo "Load: $(uptime | awk -F'load average:' '{print $2}')"
echo "Memory: $(free -h | awk 'NR==2{print $3 "/" $2}')"
echo "Disk: $(df -h / | awk 'NR==2{print $3 "/" $2 " (" $5 ")"}')"
}
# ===== PROMPT =====
export PS1='[\[\033[01;31m\]\u\[\033[00m\]@\[\033[01;32m\]\h\[\033[00m\] \[\033[01;34m\]\W\[\033[00m\]]\$ '
# ===== HISTORY =====
export HISTSIZE=100000
export HISTFILESIZE=200000
export HISTCONTROL=ignoreboth:erasedups
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
shopt -s histappend
Example 4: System-Wide Custom PATH (/etc/profile.d/custom-apps.sh)
# /etc/profile.d/custom-apps.sh
# Add custom application directories to PATH for all users
# Add local sbin directories
export PATH="/usr/local/sbin:$PATH"
# Add custom application directory
export PATH="/opt/custom-apps/bin:$PATH"
# Add shared scripts directory
export PATH="/usr/local/scripts:$PATH"
Best Practices
1. Always Source System Files First
In your personal ~/.bashrc, source the system-wide file at the beginning:
# Source system definitions first
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# Then add your customizations...
2. Use /etc/profile.d/ for System-Wide Changes
Don't edit /etc/profile directly. Create files in /etc/profile.d/ instead:
# Create a custom file
sudo vim /etc/profile.d/custom-settings.sh
# Add your settings
export CUSTOM_VAR="value"
3. Source .bashrc from .bash_profile
Ensure interactive settings work in login shells:
# In ~/.bash_profile
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
4. Check for File Existence Before Sourcing
Always check if a file exists before sourcing:
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
5. Keep Configuration Modular
Split large configurations into separate files:
# In ~/.bashrc
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
if [ -f ~/.bash_functions ]; then
. ~/.bash_functions
fi
if [ -f ~/.bash_prompt ]; then
. ~/.bash_prompt
fi
6. Comment Your Configuration
Document why you added settings:
# Enable case-insensitive tab completion for better usability
bind "set completion-ignore-case on"
# Increase history size to keep more command history
export HISTSIZE=50000
7. Backup Before Editing System Files
Always backup before editing system-wide files:
sudo cp /etc/profile /etc/profile.backup-$(date +%Y%m%d)
sudo vim /etc/profile
8. Test Changes Before Making Permanent
Test in current shell before adding to config files:
# Test an alias
alias ll='ls -lah'
# If it works, add to ~/.bashrc
echo "alias ll='ls -lah'" >> ~/.bashrc
9. Use Full Paths in Scripts
In system-wide files, use full paths for reliability:
# Good
/usr/bin/echo "Starting initialization..."
# Avoid
echo "Starting initialization..."
10. Avoid Infinite Loops
Be careful when sourcing files that source other files:
# Don't do this in ~/.bashrc:
source ~/.bash_profile # This might source .bashrc again!
Common Pitfalls and How to Avoid Them
Pitfall 1: Editing the Wrong File
Problem: Adding an alias to .bash_profile instead of .bashrc.
# Wrong - won't work in new terminal windows
# In ~/.bash_profile:
alias ll='ls -lah'
Solution: Aliases belong in .bashrc:
# Correct - in ~/.bashrc:
alias ll='ls -lah'
Pitfall 2: Not Sourcing .bashrc from .bash_profile
Problem: Settings in .bashrc don't work when you SSH in (login shell).
Solution: Add to .bash_profile:
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
Pitfall 3: Using export in /etc/environment
Problem: /etc/environment is not a shell script.
# Wrong - won't work in /etc/environment:
export PATH="/usr/local/bin:$PATH"
Solution: Use simple assignments:
# Correct - in /etc/environment:
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Pitfall 4: Forgetting to Reload Configuration
Problem: Changes don't take effect in current shell.
Solution: Source the file after editing:
# After editing ~/.bashrc:
source ~/.bashrc
Pitfall 5: PATH Gets Longer with Each New Shell
Problem: Adding to PATH in .bashrc without checking if it's already there.
# Wrong - PATH grows with each new shell:
export PATH="$HOME/bin:$PATH"
Solution: Check before adding, or put in .bash_profile instead:
# Option 1: Check first (in .bashrc)
if [[ ":$PATH:" != *":$HOME/bin:"* ]]; then
export PATH="$HOME/bin:$PATH"
fi
# Option 2: Put in .bash_profile (sourced once at login)
# In ~/.bash_profile:
export PATH="$HOME/bin:$PATH"
Pitfall 6: Syntax Errors Break Login
Problem: A syntax error in .bash_profile can prevent login.
Solution:
- Test syntax before sourcing:
bash -n ~/.bash_profile - Keep a backup:
cp ~/.bash_profile ~/.bash_profile.backup - If locked out, boot to single-user mode or use SSH with a different shell:
ssh user@host /bin/sh
Pitfall 7: Overriding Important System Commands
Problem: Aliasing system commands can cause issues.
# Dangerous - this will confuse scripts:
alias ls='ls --color=auto -lah'
Solution: Use different names or be very careful:
# Better - use a different name:
alias ll='ls --color=auto -lah'
# Or check if running interactively:
if [ -t 1 ]; then
alias ls='ls --color=auto'
fi
Pitfall 8: Assuming Files Exist
Problem: Sourcing files that might not exist.
# Wrong - fails if file doesn't exist:
source /etc/bashrc
Solution: Always check first:
# Correct:
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
Bash Startup Files Cheat Sheet
File Execution Summary
LOGIN SHELL (ssh, su -, login console):
1. /etc/profile
└─> /etc/profile.d/*.sh
2. ~/.bash_profile (or ~/.bash_login or ~/.profile)
└─> often sources ~/.bashrc
3. On exit: ~/.bash_logout
NON-LOGIN SHELL (new terminal window):
1. /etc/bashrc (or /etc/bash.bashrc)
2. ~/.bashrc
Quick Reference
# Check if login shell
echo $0 # -bash = login shell, bash = non-login
shopt login_shell # on = login, off = non-login
# Reload configuration
source ~/.bashrc
source ~/.bash_profile
# Test syntax without executing
bash -n ~/.bashrc
# View your current PATH
echo $PATH | tr ':' '\n'
# Find which file defines an alias
type alias_name
type -a alias_name # Show all definitions
# List all current aliases
alias
# List all shell functions
declare -F
# View environment variables
env
printenv
File Purposes
/etc/environment # System env vars (all shells)
/etc/profile # System login init (bash)
/etc/profile.d/*.sh # Modular system login scripts
/etc/bashrc # System interactive init (bash)
~/.bash_profile # User login init
~/.bashrc # User interactive init
~/.bash_logout # User logout cleanup
Common Tasks
# Add alias
echo "alias ll='ls -lah'" >> ~/.bashrc && source ~/.bashrc
# Add to PATH (login shells)
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bash_profile && source ~/.bash_profile
# Create system-wide PATH addition
echo 'export PATH="/opt/myapp/bin:$PATH"' | sudo tee /etc/profile.d/myapp.sh
# Customize prompt
echo 'export PS1="\u@\h:\w\$ "' >> ~/.bashrc && source ~/.bashrc
# Set environment variable
echo 'export EDITOR=vim' >> ~/.bash_profile && source ~/.bash_profile
Key Takeaways
Let's summarize the critical concepts from this comprehensive guide:
- Login vs Non-Login Shells - Understanding this distinction is fundamental to knowing which files are executed and when
- ~/.bashrc is for interactive settings - Aliases, functions, prompt customization, history settings
- ~/.bash_profile is for login initialization - Environment variables, PATH, one-time login tasks
- Source .bashrc from .bash_profile - Ensures interactive settings work in login shells
- Use /etc/profile.d/ for system-wide changes - Don't edit /etc/profile directly
- source command reloads configuration - Apply changes without logging out
- Check file existence before sourcing - Prevents errors if files are missing
- PATH should be set in login files - To avoid growing PATH with each new shell
- Keep configuration modular - Split into separate files for organization
- Always backup before editing - Especially for system-wide files
- /etc/environment is NOT a shell script - Use simple KEY=value format only
- Test changes before making permanent - Verify in current shell first
- Comment your configuration - Future you will thank present you
- Different distributions use different file locations - RHEL uses /etc/bashrc, Debian uses /etc/bash.bashrc
- Order matters - Files are executed in a specific sequence
For LFCS certification, make sure you can:
- ✅ Explain the difference between login and non-login shells
- ✅ Identify which configuration files are executed for each shell type
- ✅ Edit user-specific configuration files (~/.bashrc, ~/.bash_profile)
- ✅ Create system-wide configuration in /etc/profile.d/
- ✅ Reload configuration with the source command
- ✅ Troubleshoot configuration issues
- ✅ Create aliases and functions in the appropriate files
- ✅ Modify PATH for users and system-wide
- ✅ Set environment variables appropriately
Practice Labs
Let's practice everything we've learned with hands-on labs!
Warm-up Labs
Lab 1: Check if your current shell is a login shell
Task: Determine if your current shell is a login or non-login shell using two different methods.
Solution:
# Method 1: Check $0
echo $0
# If output is "-bash" (with leading dash) = login shell
# If output is "bash" (no dash) = non-login shell
# Method 2: Use shopt
shopt login_shell
# If output is "login_shell on" = login shell
# If output is "login_shell off" = non-login shell
# Method 3: Check with grep
shopt | grep login_shell
Verification:
- SSH sessions show login shell
- New terminal windows typically show non-login shell
Lab 2: View your current .bashrc file
Task: Display the contents of your ~/.bashrc file and count how many lines it contains.
Solution:
# View the file
cat ~/.bashrc
# Count lines
wc -l ~/.bashrc
# View with line numbers
cat -n ~/.bashrc
# Page through it
less ~/.bashrc
# Search for specific content (like aliases)
grep alias ~/.bashrc
Expected output: Shows your current bash configuration
Lab 3: List all currently defined aliases
Task: Display all aliases currently active in your shell.
Solution:
# List all aliases
alias
# Count how many aliases you have
alias | wc -l
# Search for specific alias
alias | grep ls
# Check if a specific alias exists
alias ll 2>/dev/null || echo "Alias 'll' not defined"
Expected output: List of all current aliases
Lab 4: Create a temporary alias and test it
Task: Create an alias called mydate that shows the current date in YYYY-MM-DD format. Verify it works, then verify it disappears in a new shell.
Solution:
# Create the alias
alias mydate='date +%Y-%m-%d'
# Test it
mydate
# Output: 2026-01-12
# Verify it exists
alias mydate
# Start a new shell
bash
# Try the alias - it won't work
mydate
# Output: bash: mydate: command not found
# Exit the subshell
exit
# The alias still works in original shell
mydate
# Output: 2026-01-12
Learning: Temporary aliases only exist in the current shell
Lab 5: View the /etc/profile file
Task: Examine the system-wide login initialization file.
Solution:
# View the file
cat /etc/profile
# View with pagination
less /etc/profile
# See if it sources /etc/profile.d/
grep profile.d /etc/profile
# Count lines
wc -l /etc/profile
# Check permissions
ls -l /etc/profile
Expected output: System-wide bash login configuration
Core Practice Labs
Lab 6: Add a permanent alias to .bashrc
Task: Create an alias ll that shows a long listing with human-readable sizes and colors, make it permanent, and reload your configuration.
Solution:
# First, test the alias
alias ll='ls -lah --color=auto'
# Test it
ll
# If it works, make it permanent
echo "alias ll='ls -lah --color=auto'" >> ~/.bashrc
# Reload .bashrc
source ~/.bashrc
# Verify it's in the file
grep "alias ll" ~/.bashrc
# Test in a new shell
bash
ll
exit
Verification: The ll alias works in new shells
Lab 7: Create a shell function to create and enter directories
Task: Create a function called mkcd that creates a directory and immediately changes into it. Make it permanent in .bashrc.
Solution:
# Create the function (test first)
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Test it
mkcd /tmp/test-dir
pwd # Should show /tmp/test-dir
cd ~
# Make it permanent - add to .bashrc
cat >> ~/.bashrc << 'EOF'
# Create directory and cd into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
EOF
# Reload configuration
source ~/.bashrc
# Test in new shell
bash
mkcd /tmp/another-test
pwd
exit
Verification: Function works in new shells
Lab 8: Add a directory to your PATH
Task: Create a ~/bin directory, add it to your PATH in .bash_profile, and verify scripts there can be executed from anywhere.
Solution:
# Create the directory
mkdir -p ~/bin
# Create a test script
cat > ~/bin/hello << 'EOF'
#!/bin/bash
echo "Hello from ~/bin/hello!"
EOF
# Make it executable
chmod +x ~/bin/hello
# Try to run it - won't work yet
hello
# Output: bash: hello: command not found
# Add ~/bin to PATH in .bash_profile
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bash_profile
# Reload
source ~/.bash_profile
# Now it should work
hello
# Output: Hello from ~/bin/hello!
# Verify PATH
echo $PATH | tr ':' '\n' | grep bin
# Test in new login shell
bash -l
hello
exit
Verification: Script runs from any directory
Lab 9: Customize your command prompt
Task: Change your prompt to show username, hostname, and current directory in color.
Solution:
# Test the prompt first
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
# If you like it, make it permanent in .bashrc
echo "export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '" >> ~/.bashrc
# Reload
source ~/.bashrc
# Test different colors (red user):
export PS1='\[\033[01;31m\]\u\[\033[00m\]@\[\033[01;32m\]\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
# Explanation:
# \u = username
# \h = hostname
# \w = full path
# \W = basename only
# \033[01;32m = green color
# \033[01;34m = blue color
# \033[00m = reset color
Verification: Prompt shows in color with user@host:path$
Lab 10: Set your default editor
Task: Set vim as your default editor for all applications using the EDITOR and VISUAL environment variables.
Solution:
# Test first
export EDITOR=vim
export VISUAL=vim
# Verify
echo $EDITOR
echo $VISUAL
# Make permanent in .bash_profile
cat >> ~/.bash_profile << 'EOF'
# Set default editor
export EDITOR=vim
export VISUAL=vim
EOF
# Reload
source ~/.bash_profile
# Test with crontab (will use your editor)
crontab -e
# (Press :q to quit without changes)
# Verify in new shell
bash -l
echo $EDITOR
exit
Verification: Applications use vim as editor
Lab 11: Configure history settings
Task: Increase history size to 10,000 commands, add timestamps, and configure to ignore duplicates.
Solution:
# Test the settings first
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoreboth:erasedups
export HISTTIMEFORMAT="%F %T "
# View history with timestamps
history | tail -20
# Make permanent in .bashrc
cat >> ~/.bashrc << 'EOF'
# Enhanced history configuration
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoreboth:erasedups
export HISTTIMEFORMAT="%F %T "
shopt -s histappend
EOF
# Reload
source ~/.bashrc
# Verify settings
echo $HISTSIZE
echo $HISTFILESIZE
echo $HISTCONTROL
echo $HISTTIMEFORMAT
# Run some commands and check history
ls
pwd
date
history | tail -5
Verification: History shows timestamps and ignores duplicates
Lab 12: Create a logout script
Task: Create a .bash_logout file that clears the screen and logs your logout time.
Solution:
# Create .bash_logout
cat > ~/.bash_logout << 'EOF'
# Clear screen for privacy
clear
# Log logout time
echo "Logged out at: $(date)" >> ~/logout.log
# Display goodbye message
echo "Goodbye, $USER!"
EOF
# Test by logging out and back in
# For SSH:
exit
# Then reconnect
# Check the log file
cat ~/logout.log
# Alternative: Test in a login subshell
bash -l
exit
# Check output
Verification: Screen clears and logout is logged
Lab 13: Create a system-wide PATH addition
Task: Add /opt/custom/bin to PATH for all users by creating a file in /etc/profile.d/.
Solution:
# Create the directory (simulate custom app)
sudo mkdir -p /opt/custom/bin
# Create a test script
echo '#!/bin/bash' | sudo tee /opt/custom/bin/custom-app
echo 'echo "Custom app running!"' | sudo tee -a /opt/custom/bin/custom-app
sudo chmod +x /opt/custom/bin/custom-app
# Try to run - won't work yet
custom-app
# Output: bash: custom-app: command not found
# Create system-wide PATH configuration
sudo tee /etc/profile.d/custom-path.sh << 'EOF'
# Add custom application directory to PATH
export PATH="/opt/custom/bin:$PATH"
EOF
# Make it executable
sudo chmod +x /etc/profile.d/custom-path.sh
# For the change to take effect, start a new login shell
bash -l
# Now test
custom-app
# Output: Custom app running!
# Verify PATH
echo $PATH | tr ':' '\n' | grep custom
exit
Verification: Custom app runs for all users after login
Lab 14: Source .bashrc from .bash_profile
Task: Ensure your .bash_profile sources .bashrc so interactive settings work in login shells.
Solution:
# Check current .bash_profile
cat ~/.bash_profile
# Backup first
cp ~/.bash_profile ~/.bash_profile.backup
# Add sourcing of .bashrc (if not already there)
cat >> ~/.bash_profile << 'EOF'
# Source .bashrc for interactive shell settings
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
EOF
# Reload
source ~/.bash_profile
# Test: Create an alias in .bashrc
echo "alias testcmd='echo This is a test'" >> ~/.bashrc
# Start a login shell and test
bash -l
testcmd
# Output: This is a test
exit
# Verify the sourcing is in .bash_profile
grep -A 3 "Source .bashrc" ~/.bash_profile
Verification: Aliases from .bashrc work in login shells
Lab 15: Create a modular configuration
Task: Split your .bashrc into separate files for aliases, functions, and prompt configuration.
Solution:
# Create separate configuration files
# 1. Aliases file
cat > ~/.bash_aliases << 'EOF'
# Enhanced ls commands
alias ll='ls -lah --color=auto'
alias la='ls -A'
alias l='ls -CF'
# Safety aliases
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Shortcuts
alias c='clear'
alias h='history'
EOF
# 2. Functions file
cat > ~/.bash_functions << 'EOF'
# Create directory and cd into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Quick backup
backup() {
cp "$1" "$1.backup-$(date +%Y%m%d-%H%M%S)"
}
# Search history
hs() {
history | grep "$1"
}
EOF
# 3. Prompt file
cat > ~/.bash_prompt << 'EOF'
# Colorful prompt with user@host:path
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
EOF
# 4. Update .bashrc to source these files
cat >> ~/.bashrc << 'EOF'
# Source modular configuration files
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
if [ -f ~/.bash_functions ]; then
. ~/.bash_functions
fi
if [ -f ~/.bash_prompt ]; then
. ~/.bash_prompt
fi
EOF
# Reload configuration
source ~/.bashrc
# Test aliases
ll
# Test functions
mkcd /tmp/modular-test
pwd
cd ~
# Verify all files
ls -la ~/.bash_*
Verification: All aliases, functions, and prompt work correctly
Advanced Challenge Labs
Lab 16: Create a Git-aware prompt
Task: Create a custom prompt that shows the current Git branch when inside a Git repository.
Solution:
# Create the function
cat >> ~/.bash_functions << 'EOF'
# Parse Git branch for prompt
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
EOF
# Create the prompt
cat > ~/.bash_prompt << 'EOF'
# Git-aware prompt: user@host:path (branch)$
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[33m\]$(parse_git_branch)\[\033[00m\]\$ '
EOF
# Reload
source ~/.bashrc
# Test in a Git repository
cd ~
mkdir -p ~/git-test
cd ~/git-test
git init
# Notice: prompt shows (master) or (main)
# Create a branch
git checkout -b feature-branch
# Notice: prompt shows (feature-branch)
# Clean up
cd ~
rm -rf ~/git-test
Verification: Prompt shows current Git branch in yellow
Lab 17: Create an extract function for archives
Task: Create a function that can extract any type of archive automatically based on file extension.
Solution:
# Add to .bash_functions
cat >> ~/.bash_functions << 'EOF'
# Universal extract function
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.gz|*.tgz) tar xzf "$1" ;;
*.tar.bz2|*.tbz2) tar xjf "$1" ;;
*.tar.xz) tar xJf "$1" ;;
*.tar) tar xf "$1" ;;
*.zip) unzip "$1" ;;
*.gz) gunzip "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar x "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "Unknown archive format: $1" ;;
esac
else
echo "File not found: $1"
fi
}
EOF
# Reload
source ~/.bashrc
# Test with different archive types
cd /tmp
# Create test tar.gz
mkdir test-dir
echo "test content" > test-dir/file.txt
tar czf test.tar.gz test-dir/
rm -rf test-dir
# Extract using the function
extract test.tar.gz
# Verify
ls test-dir/
cat test-dir/file.txt
# Clean up
rm -rf test-dir test.tar.gz
Verification: Function extracts various archive formats automatically
Lab 18: Create a system information function
Task: Create a function that displays a formatted system information summary.
Solution:
# Add to .bash_functions
cat >> ~/.bash_functions << 'EOF'
# System information summary
sysinfo() {
echo "========================================="
echo " System Information"
echo "========================================="
echo "Hostname : $(hostname)"
echo "Kernel : $(uname -r)"
echo "OS : $(cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"' || uname -s)"
echo "Uptime : $(uptime -p 2>/dev/null || uptime)"
echo "Load Average : $(uptime | awk -F'load average:' '{print $2}')"
echo "Memory (Used) : $(free -h 2>/dev/null | awk 'NR==2{print $3 "/" $2}' || echo 'N/A')"
echo "Disk (/) : $(df -h / 2>/dev/null | awk 'NR==2{print $3 "/" $2 " (" $5 ")"}' || echo 'N/A')"
echo "CPU Cores : $(nproc 2>/dev/null || echo 'N/A')"
echo "Current User : $USER"
echo "Shell : $SHELL"
echo "========================================="
}
EOF
# Reload
source ~/.bashrc
# Test the function
sysinfo
# Create an alias for quick access
echo "alias si='sysinfo'" >> ~/.bash_aliases
source ~/.bash_aliases
# Test the alias
si
Verification: Displays formatted system information
Lab 19: Prevent duplicate PATH entries
Task: Create a function that safely adds directories to PATH only if they're not already present.
Solution:
# Create the function in .bash_profile
cat >> ~/.bash_profile << 'EOF'
# Safe PATH addition function
pathadd() {
if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$1:$PATH"
echo "Added to PATH: $1"
else
if [ ! -d "$1" ]; then
echo "Directory does not exist: $1"
else
echo "Already in PATH: $1"
fi
fi
}
EOF
# Reload
source ~/.bash_profile
# Test the function
echo "Current PATH entries:"
echo $PATH | tr ':' '\n'
# Try adding a directory
mkdir -p ~/test-bin
pathadd ~/test-bin
# Output: Added to PATH: /home/user/test-bin
# Try adding the same directory again
pathadd ~/test-bin
# Output: Already in PATH: /home/user/test-bin
# Try adding a non-existent directory
pathadd ~/does-not-exist
# Output: Directory does not exist: /home/user/does-not-exist
# Verify PATH
echo $PATH | tr ':' '\n' | grep test-bin
Verification: Directories added only once to PATH
Lab 20: Complete bash configuration with all best practices (FINAL LAB!)
Task: Create a complete, production-ready bash configuration following all best practices from this guide. This is your final lab - make it comprehensive!
Solution:
# ===== STEP 1: Backup existing configuration =====
cd ~
mkdir -p bash-config-backup
cp .bashrc .bash_profile .bash_logout bash-config-backup/ 2>/dev/null || true
echo "Backup created in ~/bash-config-backup/"
# ===== STEP 2: Create modular configuration files =====
# .bash_aliases
cat > ~/.bash_aliases << 'EOF'
# ~/.bash_aliases - Personal command aliases
# Safety first
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Enhanced ls
alias ll='ls -lah --color=auto'
alias la='ls -A --color=auto'
alias l='ls -CF --color=auto'
# Directory navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
# Git shortcuts
alias gs='git status'
alias ga='git add'
alias gc='git commit'
alias gp='git push'
alias gl='git log --oneline --graph --all'
alias gd='git diff'
# System shortcuts
alias c='clear'
alias h='history'
alias ports='netstat -tulanp'
alias listening='ss -tulpn'
alias psg='ps aux | grep -v grep | grep -i -e VSZ -e'
# Disk usage
alias df='df -h'
alias du='du -h'
alias free='free -h'
EOF
# .bash_functions
cat > ~/.bash_functions << 'EOF'
# ~/.bash_functions - Personal shell functions
# Create directory and cd into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Quick backup with timestamp
backup() {
if [ -f "$1" ]; then
cp "$1" "$1.backup-$(date +%Y%m%d-%H%M%S)"
echo "Backed up: $1"
else
echo "File not found: $1"
fi
}
# Universal extract function
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.gz|*.tgz) tar xzf "$1" ;;
*.tar.bz2|*.tbz2) tar xjf "$1" ;;
*.tar.xz) tar xJf "$1" ;;
*.tar) tar xf "$1" ;;
*.zip) unzip "$1" ;;
*.gz) gunzip "$1" ;;
*.bz2) bunzip2 "$1" ;;
*) echo "Unknown format: $1" ;;
esac
else
echo "File not found: $1"
fi
}
# Search in command history
hs() {
history | grep "$1"
}
# System information summary
sysinfo() {
echo "========================================="
echo " System Information"
echo "========================================="
echo "Hostname : $(hostname)"
echo "Kernel : $(uname -r)"
echo "Uptime : $(uptime -p 2>/dev/null || uptime)"
echo "Load Average : $(uptime | awk -F'load average:' '{print $2}')"
echo "Memory : $(free -h 2>/dev/null | awk 'NR==2{print $3 "/" $2}' || echo 'N/A')"
echo "Disk (/) : $(df -h / 2>/dev/null | awk 'NR==2{print $3 "/" $2 " (" $5 ")"}' || echo 'N/A')"
echo "Current User : $USER"
echo "========================================="
}
# Safe PATH addition
pathadd() {
if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$1:$PATH"
fi
}
EOF
# .bash_prompt
cat > ~/.bash_prompt << 'EOF'
# ~/.bash_prompt - Prompt customization
# Git branch parser
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
# Colorful Git-aware prompt
# Format: user@host:path (git-branch)$
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[33m\]$(parse_git_branch)\[\033[00m\]\$ '
EOF
# ===== STEP 3: Create main .bashrc =====
cat > ~/.bashrc << 'EOF'
# ~/.bashrc - Main interactive shell configuration
# Source system-wide definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
elif [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac
# ===== HISTORY CONFIGURATION =====
export HISTSIZE=50000
export HISTFILESIZE=100000
export HISTCONTROL=ignoreboth:erasedups
export HISTTIMEFORMAT="%F %T "
shopt -s histappend
# ===== SHELL OPTIONS =====
# Check window size after each command
shopt -s checkwinsize
# Enable recursive globbing with **
shopt -s globstar 2>/dev/null
# ===== TAB COMPLETION =====
# Case-insensitive tab completion
bind "set completion-ignore-case on" 2>/dev/null
# Show all matches if ambiguous
bind "set show-all-if-ambiguous on" 2>/dev/null
# ===== SOURCE MODULAR FILES =====
# Load aliases
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# Load functions
if [ -f ~/.bash_functions ]; then
. ~/.bash_functions
fi
# Load prompt configuration
if [ -f ~/.bash_prompt ]; then
. ~/.bash_prompt
fi
EOF
# ===== STEP 4: Create .bash_profile =====
cat > ~/.bash_profile << 'EOF'
# ~/.bash_profile - Login shell initialization
# ===== ENVIRONMENT VARIABLES =====
export EDITOR=vim
export VISUAL=vim
export PAGER=less
# ===== PATH CONFIGURATION =====
# Load pathadd function if available
if [ -f ~/.bash_functions ]; then
. ~/.bash_functions
fi
# Add user bin directories to PATH
pathadd "$HOME/bin"
pathadd "$HOME/.local/bin"
# ===== SOURCE .BASHRC =====
# Ensure interactive settings work in login shells
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# ===== LOGIN MESSAGE =====
echo "Welcome back, $USER!"
echo "Login time: $(date '+%Y-%m-%d %H:%M:%S')"
EOF
# ===== STEP 5: Create .bash_logout =====
cat > ~/.bash_logout << 'EOF'
# ~/.bash_logout - Cleanup on logout
# Clear screen for privacy
clear
# Log logout time
echo "Logout: $(date '+%Y-%m-%d %H:%M:%S')" >> ~/.bash_login_history
# Display goodbye message
echo "Goodbye, $USER!"
EOF
# ===== STEP 6: Create user bin directory =====
mkdir -p ~/bin
# Create a sample script
cat > ~/bin/myinfo << 'EOF'
#!/bin/bash
# Display current user information
echo "User: $USER"
echo "Home: $HOME"
echo "Shell: $SHELL"
echo "Current directory: $PWD"
EOF
chmod +x ~/bin/myinfo
# ===== STEP 7: Reload configuration =====
source ~/.bash_profile
# ===== STEP 8: Test everything =====
echo ""
echo "========================================="
echo " Configuration Installation Complete!"
echo "========================================="
echo ""
echo "Testing installed components..."
echo ""
# Test aliases
echo "✓ Aliases loaded: $(alias | wc -l) aliases defined"
# Test functions
echo "✓ Functions loaded: mkcd, backup, extract, hs, sysinfo, pathadd"
# Test prompt
echo "✓ Custom prompt configured"
# Test history
echo "✓ History size: $HISTSIZE commands"
# Test PATH
echo "✓ Custom PATH: ~/bin and ~/.local/bin added"
# Test user script
echo "✓ User script: ~/bin/myinfo created"
echo ""
echo "Run these commands to test:"
echo " ll # Enhanced ls"
echo " sysinfo # System information"
echo " myinfo # User information"
echo " mkcd testdir # Create and enter directory"
echo ""
echo "Configuration files:"
echo " ~/.bashrc # Main interactive config"
echo " ~/.bash_profile # Login shell config"
echo " ~/.bash_aliases # Your aliases"
echo " ~/.bash_functions # Your functions"
echo " ~/.bash_prompt # Prompt configuration"
echo ""
echo "Backup saved in: ~/bash-config-backup/"
echo "========================================="
Verification Steps:
# 1. Test aliases
ll
c # clear screen
# 2. Test functions
sysinfo
mkcd /tmp/final-test
pwd
cd ~
# 3. Test user script
myinfo
# 4. Test history
h | tail
hs bash
# 5. Test PATH
echo $PATH | tr ':' '\n' | grep -E 'bin|local'
# 6. Test Git prompt (if in a git repo)
cd ~/some-git-repo
# Prompt should show (branch-name)
# 7. View configuration
cat ~/.bashrc
cat ~/.bash_profile
cat ~/.bash_aliases
cat ~/.bash_functions
cat ~/.bash_prompt
# 8. Test in new shell
bash
ll
sysinfo
exit
# 9. Test login shell
bash -l
# Should see welcome message
exit
# 10. Verify backup
ls -la ~/bash-config-backup/
Congratulations! You've completed all 20 labs and created a professional bash configuration! 🎉
What's Next?
🎉 CONGRATULATIONS! You've completed ALL 52 posts in the LFCS Phase 1 series! 🎉
You've covered an incredible amount of material:
- Linux basics and navigation
- User and permission management
- File operations and text processing
- Networking and remote access
- System administration fundamentals
- Shell customization and productivity
You now have a solid foundation in Linux system administration and are well-prepared for the LFCS certification exam!
Next Steps in Your LFCS Journey
- Review and Practice: Go back through the posts and practice labs regularly
- Build Real Projects: Apply your knowledge to real-world scenarios
- Prepare for LFCS Exam: Review the official LFCS exam domains
- Continue Learning: Explore advanced topics like automation, containers, and orchestration
Additional Resources
- LFCS Official Documentation: https://training.linuxfoundation.org/certification/linux-foundation-certified-sysadmin-lfcs/
- Linux Documentation Project: https://tldp.org/
- Bash Manual:
man bash - Bash Guide for Beginners: https://tldp.org/LDP/Bash-Beginners-Guide/html/
Final Summary
In this comprehensive final post, we covered:
- ✅ Login vs non-login shells - The fundamental distinction that determines which files are executed
- ✅ System-wide configuration - /etc/environment, /etc/profile, /etc/profile.d/, /etc/bashrc
- ✅ User-specific configuration - ~/.bash_profile, ~/.bashrc, ~/.bash_logout
- ✅ Execution order - The precise sequence bash follows when initializing
- ✅ source command - Reloading configuration without logging out
- ✅ Best practices - How to organize and maintain your bash configuration
- ✅ Common pitfalls - Mistakes to avoid and how to fix them
- ✅ Real-world examples - Complete configurations for developers and sysadmins
- ✅ 20 hands-on practice labs - From basic to advanced configuration scenarios
You now have complete mastery of bash startup files and configuration. You can customize your shell environment efficiently, troubleshoot configuration issues, and manage both user-specific and system-wide settings with confidence.
Thank you for following along with this entire 52-part series! Your dedication to learning Linux system administration is commendable. Best of luck with your LFCS certification and your Linux journey! 🚀
Quick Reference Card
# ===== FILE LOCATIONS =====
/etc/environment # System env vars (all shells)
/etc/profile # System login init
/etc/profile.d/*.sh # Modular system login scripts
/etc/bashrc # System interactive init (RHEL/CentOS)
/etc/bash.bashrc # System interactive init (Debian/Ubuntu)
~/.bash_profile # User login init
~/.bashrc # User interactive init
~/.bash_logout # User logout cleanup
# ===== EXECUTION ORDER =====
# Login shell: /etc/profile → ~/.bash_profile → ~/.bash_logout (on exit)
# Non-login: /etc/bashrc → ~/.bashrc
# ===== COMMON COMMANDS =====
source ~/.bashrc # Reload configuration
bash -n ~/.bashrc # Check syntax without executing
echo $0 # Check shell type (-bash = login)
shopt login_shell # Check if login shell
alias # List all aliases
type command_name # Show command type/location
env # Show environment variables
# ===== QUICK EDITS =====
vim ~/.bashrc # Edit interactive config
vim ~/.bash_profile # Edit login config
source ~/.bashrc # Apply changes
# ===== COMMON ADDITIONS =====
echo "alias ll='ls -lah'" >> ~/.bashrc
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bash_profile
echo 'export EDITOR=vim' >> ~/.bash_profile
This concludes the LFCS Phase 1 series. Happy Linux learning! 🐧

