14.4. Startup Files#

14.4.1. Common Pitfalls#

1. Startup file loops

# Bad: bashrc sources profile which sources bashrc (infinite loop)
# ~/.bashrc
source ~/.bash_profile

# ~/.bash_profile
source ~/.bashrc

# Good: Prevent re-sourcing
if [[ -n "$BASHRC_SOURCED" ]]; then
  return
fi
export BASHRC_SOURCED=1

2. Aliases don’t work in scripts

# Bad: Aliases not expanded in non-interactive shells
#!/bin/bash
alias ll='ls -lah'
ll  # Does not work (alias not expanded)

# Good: Use function or full command
#!/bin/bash
ll() { ls -lah "$@"; }
ll  # Works

3. Aliases with arguments fail

# Bad: Can't pass arguments to alias
alias backup='tar -czf backup.tar.gz'
backup mydir  # Doesn't work as expected

# Good: Use function
backup() {
  tar -czf backup.tar.gz "$@"
}
backup mydir  # Works correctly

4. PATH issues from startup files

# Bad: Appending to PATH repeatedly
export PATH="$HOME/bin:$PATH"  # Added every time

# Good: Check if already added
if [[ ":$PATH:" != *":$HOME/bin:"* ]]; then
  export PATH="$HOME/bin:$PATH"
fi

5. Startup file doesn’t exist for scripts

# Scripts don't source ~/.bashrc by default
# Need to explicitly source for environment
#!/bin/bash
source ~/.bashrc  # Load environment

# Or use bash -i for interactive shell
bash -i -c 'command'

14.4.2. Aliases and Functions#

Aliases and functions enhance productivity and provide shortcuts for common operations.

14.4.2.1. Creating and Managing Aliases#

#!/bin/bash

# Simple aliases
alias ll='ls -lah'
alias la='ls -A'
alias l='ls -CF'
alias cd..='cd ..'
alias ...='cd ../..'

# Aliases with arguments (use functions instead)
# Bad: alias grep_error='grep -i "error"'  # Can't pass args easily

# Good: Use function
grep_error() {
  grep -i "error" "$@"
}

# Conditional aliases
if [[ "$OSTYPE" == "darwin"* ]]; then
  alias ls='ls -G'  # macOS
else
  alias ls='ls --color=auto'  # Linux
fi

# Check alias
alias ll  # Show definition
type ll   # Show type (builtin, alias, function, etc.)

# Remove alias
unalias ll

# List all aliases
alias
alias | grep cd

# Safe rm (with confirmation)
alias rm='rm -i'

# Grep with useful defaults
alias grep='grep --color=auto -n'

# View large files without loading entirely
alias large_view='less +F'

14.4.2.2. Functions vs Aliases#

#!/bin/bash

# Simple alias (no arguments)
alias gst='git status'
gst  # Works

# Problem: Can't use arguments with alias easily
alias grep_logs='grep "ERROR"'
grep_logs file.txt  # Works, appends to command

# Solution: Use function
grep_logs() {
  grep "ERROR" "$@"
}
grep_logs file.txt  # Works better

# Functions with multiple operations
backup_db() {
  local db=$1
  local dest=${2:-.}
  
  echo "Backing up database: $db"
  mysqldump "$db" > "$dest/${db}-$(date +%Y%m%d).sql"
  
  if [[ $? -eq 0 ]]; then
    echo "✓ Backup successful"
  else
    echo "✗ Backup failed" >&2
    return 1
  fi
}

backup_db myapp /backups/

# Function with conditional logic
dstat() {
  local target="${1:-.}"
  
  if [[ ! -d "$target" ]]; then
    echo "Error: $target is not a directory" >&2
    return 1
  fi
  
  du -sh "$target"
}

14.4.2.3. Advanced Alias Patterns#

#!/bin/bash

# Directory navigation aliases
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 -10'

# System monitoring
alias myip='curl ifconfig.me'
alias ports='netstat -tulanp | grep LISTEN'
alias mem='free -h'
alias disk='df -h | grep -v tmpfs'

# Safety aliases
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias ln='ln -i'

# Color output
alias grep='grep --color=auto'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'

# Docker shortcuts
alias docker-stop-all='docker stop $(docker ps -q)'
alias docker-rm-all='docker rm $(docker ps -aq)'

# Make aliases persist in functions
enable_aliases_in_functions() {
  shopt -s expand_aliases
}

14.4.3. Shell Startup Files and Initialization#

Understanding startup files is crucial for environment customization and debugging shell behavior.

14.4.3.1. Startup File Hierarchy#

# LOGIN SHELL EXECUTION ORDER:
# 1. /etc/profile         (system-wide login shell setup)
# 2. ~/.bash_profile      (user login shell setup)
# 3. ~/.bash_login        (alternative if .bash_profile missing)
# 4. ~/.profile           (fallback if bash_profile/bash_login missing)

# INTERACTIVE NON-LOGIN SHELL:
# 1. /etc/bash.bashrc     (system-wide setup)
# 2. ~/.bashrc            (user interactive setup)

# NON-INTERACTIVE SHELL (scripts):
# 1. Only processes file specified by BASH_ENV variable
# 2. No startup files normally sourced

# Check what type of shell you're in
if [[ -o login ]]; then
  echo "Login shell"
fi

if [[ $- == *i* ]]; then
  echo "Interactive shell"
fi

14.4.3.2. Practical Startup File Configuration#

#!/bin/bash
# ~/.bashrc - User interactive shell startup

# Avoid running twice in chains
if [[ -n "$BASHRC_SOURCED" ]]; then
  return
fi
export BASHRC_SOURCED=1

# Source system bashrc if available
[[ -f /etc/bashrc ]] && source /etc/bashrc

# Set environment variables
export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
export EDITOR=vim
export HISTSIZE=10000
export HISTFILESIZE=20000
export HISTCONTROL=ignoredups:ignorespace

# Shell options
shopt -s histappend
shopt -s checkwinsize
shopt -s cdspell

# Aliases
alias ll='ls -lah'
alias grep='grep --color=auto'
alias df='df -h'

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

# Prompt customization
PS1='\u@\h:\W\$ '

# Load local environment if exists
[[ -f ~/.bashrc.local ]] && source ~/.bashrc.local

14.4.3.3. Environment Variables and Exports#

#!/bin/bash

# Set and export environment variables
export MY_APP_HOME="/opt/myapp"
export MY_APP_CONFIG="$MY_APP_HOME/config"
export MY_APP_LOG="$MY_APP_HOME/logs"

# Application-specific PATH
export PATH="$MY_APP_HOME/bin:$PATH"

# Development environment
export JAVA_HOME="/usr/lib/jvm/java-11"
export MAVEN_HOME="/opt/maven"
export PATH="$MAVEN_HOME/bin:$PATH"

# Prompt with status
export PROMPT_COMMAND='echo -ne "\033]0;$(whoami)@$(hostname):$(pwd)\007"'

# History configuration
shopt -s histappend
export PROMPT_COMMAND="$PROMPT_COMMAND; history -a; history -n"

# Colors for ls
if [[ -x /usr/bin/dircolors ]]; then
  test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || \
    eval "$(dircolors -b)"
  alias ls='ls --color=auto'
fi