Bash Startup Files
Table of Contents
- ~/.bashrc
- ~/.bash_profile
- ~/.bash_logout
- ~/.config/bash.d
- bash_aliases
- colors.bash
- functions.bash
- history.bash
- set the location of history file
- move previous history to current file (if any)
- Lines of history in memory
- Save 2,000,000 lines of history to disk (will have to grep ~/.bash_history for full listing)
- Append to history instead of overwrite
- Ignore redundant or space commands
- Ignore more
- Set time format
- Multiple commands on one line show up as a single line
- Append new history lines, clear the history list, re-read the history list, print prompt.
Bash (Bourne Again SHell) is pretty much universal now, almost every unix-like distributions ship bash as the default shell. This is probably because GNU uses bash as their default shell and for those who don't know, linux is GNU/linux. And the GNU collection of utilities are designed to be used with bash as the shell.
As for the flow of the configuration files:
File | Description | Login shell | Interactive, non login shell |
---|---|---|---|
/etc/profile | Usually will search for /etc/profile.d or /etc/bash.bashrc depending on the distro | Yes | No |
~/.bash_profile | After etc/profile, if not found, will search for ~.bash_login and ~/.profile in that order | Yes | No |
~/.bash_logout | after exit of login shell | Yes | No |
/etc/bash.bash_logout | Depends on the -DSYS_BASH_LOGOUT="/etc/bash.bash_logout" compilation flag. After exit of a login shell. |
Yes | No |
/etc/bash.bashrc | Depends on the -DSYS_BASHRC="/etc/bash.bashrc" compilation flag. Sources /usr/share/bash-completion/bash_completion. |
No | Yes |
~./bashrc | Per-user, after /etc/bash.bashrc. | No | Yes |
Usually, when using a terminal emulator, we must set the shell as bash --login
to ensure the startup files is called correctly for each instance of that terminal since it is run from a graphical environment (X or wayland), this is an interactive non-login shell.
An interactive login shell is started after login (via tty, not display manager like lightdm or gdm).
As for non-interactive shell is usually present when a shell script is running, it's processing a script not waiting for user input between each commands. Only the environment invoked from the parent shell is used.
The way I set my bash startup files:
- Try not to mess with the global startup files
- Respect the /etc/profile and the likes, they are configured to be used by many other applications. And the distribution maintaners usually sets some variables for that purpose.
- Modular
- It's basically a script, separate each uses to a specific file.
- Simple
- Should work on many other distributions that uses bash, I use this configuration for archlinux, ubuntu, debian, even freebsd.
~/.bashrc
do nothing if non-interactive
For when run as a script.
# If not running interactively, don't do anything [[ $- != *i* ]] && return if [[ -f "/etc/bashrc" ]] ; then source /etc/bashrc elif [[ -f "/etc/bash.bashrc" ]]; then source /etc/bash.bashrc fi
bash directories
I'm separating the configuration into smaller files. XDG_CONFIG_HOME
and XDG_DATA_HOME
should be set by the system, I also declared them in my repository for common shells.
# setup directory for everything bash-related BASH_DIR="${XDG_CONFIG_HOME:-~/.config}"/bash.d BASH_DATA="${XDG_DATA_HOME:-~/.local/share}"/bash [[ ! -d "${BASH_DIR}" ]] && mkdir -p "$BASH_DIR" [[ ! -d "${BASH_DATA}" ]] && mkdir -p "$BASH_DATA"
shell options
There's many more shell options for bash, but I don't use them much.
# check the window size after each command and, if necessary, # update the values of LINES and COLUMNS. shopt -s checkwinsize
aliases
# Alias definitions. # You may want to put all your additions into a separate file like # $BASH_DIR/bash_aliases, instead of adding them here directly. # See /usr/share/doc/bash-doc/examples in the bash-doc package. if [[ -f "${BASH_DIR}/bash_aliases" ]]; then . "${BASH_DIR}"/bash_aliases fi
Note that in archlinux the /usr/share/doc/bash-doc is not exists
completion
if [[ -z "${BASH_COMPLETION_VERSINFO}" ]];then # enable programmable completion features (you don't need to enable # this, if it's already enabled in /etc/bash.bashrc and /etc/profile # sources /etc/bash.bashrc). if [[ -f /etc/bash_completion ]] && ! shopt -oq posix; then . /etc/bash_completion elif [[ -f /usr/share/bash_completion/bash_completion ]] && ! shopt -oq posix; then . /usr/share/bash_completion/bash_completion elif [[ -f /usr/local/share/bash_completion/bash_completion ]] && ! shopt -oq posix; then . /usr/local/share/bash_completion/bash_completion elif [[ -f /etc/bash/bashrc.d/bash_completion.sh ]] && ! shopt -oq posix; then . /etc/bash/bashrc.d/bash_completion.sh fi fi # user completion files if [[ -d "{$BASH_DIR}/bash_completion.d" ]]; then for i in "${BASH_DIR}"/bash_completion.d/*.bash; do if [[ -r ${i} ]]; then . "${i}" fi done unset i fi
This should cover every location for bash completion.
color prompt
# color prompt, this is gentoo bashrc file [[ -f "${BASH_DIR}/colors.bash" ]] && . "${BASH_DIR}"/colors.bash
Taken from gentoo i guess…
history
# history [[ -f "${BASH_DIR}/history.bash" ]] && . "${BASH_DIR}"/history.bash
setup history
custom functions
# functions [[ -f "${BASH_DIR}/functions.bash" ]] && . "${BASH_DIR}"/functions.bash
Put custom functions in a file.
freebsd specifics
if [[ "${DISTRO}" != "freebsd" ]]; then iatest=$(expr index "$-" i) if [[ ${iatest} -gt 0 ]]; then # Disable the bell bind "set bell-style visible"; # Ignore case on auto-completion # Note: bind used instead of sticking these in .inputrc bind "set completion-ignore-case on"; # Show auto-completion list automatically, without double tab bind "set show-all-if-ambiguous On"; fi fi
Some settings specific to FreeBSD.
history navigation with ctrl-r
# Allow ctrl-S for history navigation (with ctrl-R) stty -ixon
Enable CTRL-s and CTRL-r
local override
# local bashrc if [[ -f "${BASH_DIR}/bashrc.local" ]];then . "${BASH_DIR}"/bashrc.local fi # End ~/.bashrc
In case I need to override all or some settings above, I'll put them in this file.
~/.bash_profile
source ~/etc/profile
[[ -f /etc/profile ]] && source /etc/profile
Making sure that /etc/profile is also sourced, the only downside is some variables, such as PATH, would be declared multiple times. Depending on how the PATH is set, the variable will have repeated paths. But in my common shell repository I make sure that each item is unique.
This differs in several Linux distribution.
# in the actual startup sequences, "~/.bash_profile" is invoked # before "~/.profile" so we need to source "~/.profile" first. [[ -f ${HOME}/.profile ]] && source "${HOME}"/.profile [[ -f ${HOME}/.bashrc ]] && source "${HOME}"/.bashrc
local override
# local bash_profile # ${BASH_DIR} should already set by now [[ -f ${BASH_DIR}/bash_profile.local ]] && source "${BASH_DIR}"/bash_profile.local # End ~/.bash_profile
Again, in case I need to override all or some settings above…
~/.bash_logout
clear console on logout
# when leaving the console clear the screen to increase privacy if [ "$SHLVL" = 1 ]; then #[ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q [ -x /usr/bin/clear ] && /usr/bin/clear fi # End ~/.bash_logout
~/.config/bash.d
bash_aliases
Most of the coreutils
programs have a syntax for color.
color support of most of the coreutils
# enable color support of ls and also add handy aliases if [ -x /usr/bin/dircolors ]; then test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" alias ls='ls --color=auto' #alias dir='dir --color=auto' #alias vdir='vdir --color=auto' #BSD#@export CLICOLOR=1 #GNU#@alias ls='ls --color=auto' export CLICOLOR=1 alias grep='grep --color=auto' alias fgrep='fgrep --color=auto' alias egrep='egrep --color=auto' alias diff='diff --color=auto' alias ip='ip -color=auto' fi
ls aliases
# some more ls aliases alias ll='ls -alF' alias la='ls -A' alias l='ls -CF'
Some useful ls aliases.
alert
# Add an "alert" alias for long running commands. Use like so: # sleep 10; alert alias alert='notify-send --expire-time=2000 --urgency=low --icon="$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' alias xp='xprop | grep "WM_WINDOW_ROLE\|WM_CLASS" && echo "WM_CLASS(STRING) = \"NAME\", \"CLASS\""'
I don't think I've ever use this anymore, but it still nice to have
colors.bash
PS1
Set PS1 when the terminal support it.
#!/usr/bin/env bash # Set colorful PS1 only on colorful terminals. # dircolors --print-database uses its own built-in database # instead of using /etc/DIR_COLORS. Try to use the external file # first to take advantage of user additions. # We run dircolors directly due to its changes in file syntax and # terminal name patching. for sh in /etc/bash/bashrc.d/* ; do [[ -r ${sh} ]] && source "${sh}" done use_color=false if type -P dircolors >/dev/null ; then # Enable colors for ls, etc. Prefer ~/.dir_colors #64489 LS_COLORS= if [[ -f ~/.dir_colors ]] ; then eval "$(dircolors -b ~/.dir_colors)" elif [[ -f /etc/DIR_COLORS ]] ; then eval "$(dircolors -b /etc/DIR_COLORS)" else eval "$(dircolors -b)" fi # Note: We always evaluate the LS_COLORS setting even when it's the # default. If it isn't set, then `ls` will only colorize by default # based on file attributes and ignore extensions (even the compiled # in defaults of dircolors). #583814 if [[ -n ${LS_COLORS:+set} ]] ; then use_color=true else # Delete it if it's empty as it's useless in that case. unset LS_COLORS fi else # Some systems (e.g. BSD & embedded) don't typically come with # dircolors so we need to hardcode some terminals in here. case ${TERM} in [aEkx]term*|rxvt*|gnome*|konsole*|screen*|cons25|*color|linux) use_color=true;; esac fi # bash prompt variables # # \a # A bell character. # \d # The date, in "Weekday Month Date" format (e.g., "Tue May 26"). # \D{format} # The format is passed to strftime(3) and the result is inserted # into the prompt string; an empty format results in a locale-specific # time representation. The braces are required. # \e # An escape character. # \h # The hostname, up to the first ‘.’. # \H # The hostname. # \j # The number of jobs currently managed by the shell. # \l # The basename of the shell’s terminal device name. # \n # A newline. # \r # A carriage return. # \s # The name of the shell, the basename of $0 (the portion # following the final slash). # \t # The time, in 24-hour HH:MM:SS format. # \T # The time, in 12-hour HH:MM:SS format. # \@ # The time, in 12-hour am/pm format. # \A # The time, in 24-hour HH:MM format. # \u # The username of the current user. # \v # The version of Bash (e.g., 2.00) # \V # The release of Bash, version + patchlevel (e.g., 2.00.0) # \w # The current working directory, with $HOME abbreviated with a # tilde (uses the $PROMPT_DIRTRIM variable). # \W # The basename of $PWD, with $HOME abbreviated with a tilde. # \! # The history number of this command. # \# # The command number of this command. # \$ # If the effective uid is 0, #, otherwise $. # \nnn # The character whose ASCII code is the octal value nnn. # \\ # A backslash. # \[ # Begin a sequence of non-printing characters. This could be used to # embed a terminal control sequence into the prompt. # \] # End a sequence of non-printing characters. # ANSI color codes # RS="\[\033[0m\]" # reset # HC="\[\033[1m\]" # hicolor # UL="\[\033[4m\]" # underline # INV="\[\033[7m\]" # inverse background and foreground # FBLK="\[\033[30m\]" # foreground black # FRED="\[\033[31m\]" # foreground red # FGRN="\[\033[32m\]" # foreground green # FYEL="\[\033[33m\]" # foreground yellow # FBLE="\[\033[34m\]" # foreground blue # FMAG="\[\033[35m\]" # foreground magenta # FCYN="\[\033[36m\]" # foreground cyan # FWHT="\[\033[37m\]" # foreground white # BBLK="\[\033[40m\]" # background black # BRED="\[\033[41m\]" # background red # BGRN="\[\033[42m\]" # background green # BYEL="\[\033[43m\]" # background yellow # BBLE="\[\033[44m\]" # background blue # BMAG="\[\033[45m\]" # background magenta # BCYN="\[\033[46m\]" # background cyan # BWHT="\[\033[47m\]" # background white # txtblk='\e[0;30m' # Black - Regular # txtred='\e[0;31m' # Red # txtgrn='\e[0;32m' # Green # txtylw='\e[0;33m' # Yellow # txtblu='\e[0;34m' # Blue # txtpur='\e[0;35m' # Purple # txtcyn='\e[0;36m' # Cyan # txtwht='\e[0;37m' # White # bldblk='\e[1;30m' # Black - Bold # bldred='\e[1;31m' # Red # bldgrn='\e[1;32m' # Green # bldylw='\e[1;33m' # Yellow # bldblu='\e[1;34m' # Blue # bldpur='\e[1;35m' # Purple # bldcyn='\e[1;36m' # Cyan # bldwht='\e[1;37m' # White # unkblk='\e[4;30m' # Black - Underline # undred='\e[4;31m' # Red # undgrn='\e[4;32m' # Green # undylw='\e[4;33m' # Yellow # undblu='\e[4;34m' # Blue # undpur='\e[4;35m' # Purple # undcyn='\e[4;36m' # Cyan # undwht='\e[4;37m' # White # bakblk='\e[40m' # Black - Background # bakred='\e[41m' # Red # bakgrn='\e[42m' # Green # bakylw='\e[43m' # Yellow # bakblu='\e[44m' # Blue # bakpur='\e[45m' # Purple # bakcyn='\e[46m' # Cyan # bakwht='\e[47m' # White # txtrst='\e[0m' # Text Resetreset='\033[0m' if ${use_color} ; then if [[ ${EUID} == 0 ]] ; then PS1='\[\033[01;31m\]\h\[\033[01;34m\] \w \$\[\033[00m\] ' else #PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] ' case "${DISTRO}" in # nothing fancy, just different colors for hostname # blue for arch derivatives "artix"|"arch") PS1="\[\033[0;32m\]\u\[\033[00m\]\e[2m\[\033[1m\]@\[\033[00m\]\[\033[00m\]\[\033[0;34m\]\h\[\033[00m\]\[\033[01;34m\] \w \$\[\033[00m\] " ;; "debian"|"devuan"|"ubuntu") # purple for debian and such PS1='\[\033[0;32m\]\u\[\033[00m\]\e[2m\[\033[1m\]@\[\033[00m\]\[\033[00m\]\[\033[0;35m\]\h\[\033[00m\]\[\033[01;34m\] \w \$\[\033[00m\] ' ;; "freebsd") # red PS1='\[\033[0;32m\]\u\[\033[00m\]\e[2m\[\033[1m\]@\[\033[00m\]\[\033[00m\]\[\033[1;31m\]\h\[\033[00m\]\[\033[01;34m\] \w \$\[\033[00m\] ' ;; "gentoo") # bold blue PS1='\[\033[0;32m\]\u\[\033[00m\]\e[2m\[\033[1m\]@\[\033[00m\]\[\033[00m\]\[\033[1;34m\]\h\[\033[00m\]\[\033[01;34m\] \w \$\[\033[00m\] ' esac fi else # show root@ when we don't have colors PS1+='\u@\h \w \$ ' fi # Try to keep environment pollution down, EPA loves us. unset use_color sh
functions.bash
pathremove
if [ ! "$(type pathremove >/dev/null 2>&1)" ] ; then pathremove () { local IFS=':' local NEWPATH local DIR local PATHVARIABLE=${2:-PATH} for DIR in ${!PATHVARIABLE} ; do if [ "${DIR}" != "${1}" ] ; then NEWPATH=${NEWPATH:+$NEWPATH:}$DIR fi done export "$PATHVARIABLE"="${NEWPATH}" } export -f pathremove fi
This function is for removing an entry from PATH
pathprepend
if [ ! "$(type pathprepend >/dev/null 2>&1)" ] ; then pathprepend () { pathremove "${1}" "${2}" local PATHVARIABLE=${2:-PATH} export "$PATHVARIABLE"="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}" } export -f pathprepend fi
For prepending an item into PATH
pathappend
if [ ! "$(type pathappend >/dev/null 2>&1)" ] ; then pathappend () { pathremove "${1}" "${2}" local PATHVARIABLE=${2:-PATH} export "$PATHVARIABLE"="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1" } export -f pathappend fi # end ~/.config/bash.d/functions.bash
Appending an item to PATH
history.bash
Here I set some variable that is use by the bash history. Most of these are self explainatory.
set the location of history file
export HISTFILE="${BASH_DATA}"/bash_history
move previous history to current file (if any)
if [ -f "${HOME}/.bash_history " ]; then if [ -f "${HISTFILE}" ]; then cat "${HOME}"/.bash_history >> "${HISTFILE}" fi rm "${HOME}"/.bash_history fi
Lines of history in memory
HISTSIZE=10000
Save 2,000,000 lines of history to disk (will have to grep ~/.bash_history for full listing)
HISTFILESIZE=2000000
Append to history instead of overwrite
shopt -s histappend
Ignore redundant or space commands
HISTCONTROL=ignoreboth
Ignore more
HISTIGNORE='ls:ll:ls -alh:pwd:clear:history'
Set time format
HISTTIMEFORMAT='%F %T '
Multiple commands on one line show up as a single line
shopt -s cmdhist
Append new history lines, clear the history list, re-read the history list, print prompt.
Currently unused
export PROMPT_COMMAND="history -a; history -c; history -r; ${PROMPT_COMMAND}"