Created
January 16, 2025 18:50
-
-
Save NickCrew/2aab62f046bc27721ada9a9b1f3724aa to your computer and use it in GitHub Desktop.
Minimal Bashrc - Sane defaults to carry from host to host
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# A minimal bash configuration to carry around with you | |
# | |
# !! Plus some bonus extras like fancy git prompt and bookmarks | |
# | |
# | |
set -o vi # unless you're one of *those* | |
# Globlin | |
shopt -s globstar # use **/foo to match any depth | |
shopt -s extglob # Enables *.@(TMP|tmp|~) and similar constructs | |
shopt -s failglob # don't substitute if there are no matches | |
# Exports | |
export PAGER='less' | |
export TERM=xterm-colors # Fix backspace and other weird behaviors | |
export LESS_TERMCAP_mb=$'\e[1;32m' # Make less use colors | |
export LESS_TERMCAP_md=$'\e[1;32m' | |
export LESS_TERMCAP_me=$'\e[0m' | |
export LESS_TERMCAP_se=$'\e[0m' | |
export LESS_TERMCAP_so=$'\e[01;33m' | |
export LESS_TERMCAP_ue=$'\e[0m' | |
export LESS_TERMCAP_us=$'\e[1;4;31m' | |
# History | |
HISTCONTROL=ignoreboth # ignore space and duplicates | |
HISTIGNORE='ls:bg:fg:history' | |
HISTTIMEFORMAT='%F %T ' # record timestamps | |
shopt -s histappend | |
shopt -s cmdhist | |
shopt -s histverify # Using ! history will substitute first, then execute if you hit enter a second time | |
# Completion | |
bind 'TAB:menu-complete'; | |
bind '"\e[Z" : menu-complete-backward'; | |
bind "set show-all-if-ambiguous on"; | |
bind "set menu-complete-display-prefix on"; | |
bind "set completion-ignore-case on"; | |
bind '"\e[A":history-search-backward'; | |
bind '"\e[B":history-search-forward'; | |
# Aliases | |
if [ -x /usr/bin/dircolors ]; # Enable color support of ls and also add handy aliase0s | |
then | |
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" | |
alias ls='ls --color=auto --classify --human-readable' | |
alias dir='dir --color=auto' | |
alias vdir='vdir --color=auto' | |
alias grep='grep --color=auto' | |
alias fgrep='fgrep --color=auto' | |
alias egrep='egrep --color=auto' | |
fi | |
alias la='ls -a' | |
alias ll='ls -l' | |
alias la='ls -alh' | |
alias less="less -r" | |
######### | |
# EXTRAS | |
######### | |
# | |
# Git prompt | |
# | |
USE_GIT_PROMPT=1 | |
export USE_GIT_PROMPT | |
git_prompt_toggle() { | |
if [[ $USE_GIT_PROMPT == 1 ]]; then | |
USE_GIT_PROMPT=0 | |
else | |
USE_GIT_PROMPT=1 | |
fi | |
export USE_GIT_PROMPT | |
} | |
_show_git_status() { | |
# Get the current git branch and colorize to indicate branch state | |
# branch_name+ indicates there are stash(es) | |
# branch_name? indicates there are untracked files | |
# branch_name! indicates your branches have diverged | |
local unknown untracked stash clean ahead behind staged dirty diverged | |
unknown='0;34' # blue | |
untracked='0;32' # green | |
stash='0;32' # green | |
clean='0;32' # green | |
ahead='0;33' # yellow | |
behind='0;33' # yellow | |
staged='0;96' # cyan | |
dirty='0;31' # red | |
diverged='0;31' # red | |
if [[ $TERM = *256color ]]; then | |
unknown='38;5;20' # dark blue | |
untracked='38;5;76' # mid lime-green | |
stash='38;5;76' # mid lime-green | |
clean='38;5;82' # brighter green | |
ahead='38;5;226' # bright yellow | |
behind='38;5;142' # darker yellow-orange | |
staged='38;5;214' # orangey yellow | |
dirty='38;5;202' # orange | |
diverged='38;5;196' # red | |
fi | |
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) | |
if [[ -n "$branch" ]]; then | |
if [[ "$branch" == 'HEAD' ]]; then | |
branch=$(git rev-parse --short HEAD 2>/dev/null) | |
fi | |
git_status=$(git status 2> /dev/null) | |
# If nothing changes the color, we can spot unhandled cases. | |
color=$unknown | |
if [[ $git_status =~ 'Untracked files' ]]; then | |
color=$untracked | |
branch="${branch}?" | |
fi | |
if git stash show &>/dev/null; then | |
color=$stash | |
branch="${branch}+" | |
fi | |
if [[ $git_status =~ working.*clean ]]; then | |
color=$clean | |
fi | |
if [[ $git_status =~ 'Your branch is ahead' ]]; then | |
color=$ahead | |
branch="${branch}>" | |
fi | |
if [[ $git_status =~ 'Your branch is behind' ]]; then | |
color=$behind | |
branch="${branch}<" | |
fi | |
if [[ $git_status =~ 'Changes to be committed' ]]; then | |
color=$staged | |
fi | |
if [[ $git_status =~ 'Changed but not updated' || | |
$git_status =~ 'Changes not staged' || | |
$git_status =~ 'Unmerged paths' ]]; then | |
color=$dirty | |
fi | |
if [[ $git_status =~ 'Your branch'.+diverged ]]; then | |
color=$diverged | |
branch="${branch}!" | |
fi | |
# Print the colored branch name + indicators | |
printf "\[\033[%sm\]%s" "$color" "$branch" | |
# Reset the color | |
printf "\[\033[0m\]" | |
fi | |
return 0 | |
} | |
_show_last_exit_status() { | |
# Display the exit status of the last run command | |
exit_status=$? | |
if [[ "$exit_status" -ne 0 ]]; then | |
echo "Exit $exit_status" | |
fi | |
} | |
_build_prompt() { | |
local git_status prompt_dir | |
if [[ $USE_GIT_PROMPT != 0 ]]; then | |
git_status=$(_show_git_status) | |
if [[ -n "$git_status" ]]; then | |
git_status=":${git_status}" | |
fi | |
fi | |
prompt_dir=$(basename "${PWD}") | |
color=32 | |
if [[ $EUID -eq 0 ]]; then | |
color=31 | |
fi | |
PS1="\[\e[1;${color}m\]\h\[\e[0m\] [${prompt_dir}${git_status}]\\\$ " | |
return 0 | |
} | |
PROMPT_COMMAND="_show_last_exit_status; _build_prompt; history -a;" | |
# | |
# Bookmarks | |
# | |
export MARKS=$HOME/files/marks | |
jump() { | |
if [[ -n "$1" ]]; then | |
cd -P "$MARKS/$1" 2>/dev/null | |
if [[ $? -ne 0 ]]; then | |
printf "Mark not found: %s\n" "$1" 1>&2 | |
return 2 | |
fi | |
else | |
printf "Usage: jump MARK\n" 1>&2 | |
return 1 | |
fi | |
} | |
mark() { | |
mark="$1" | |
if [[ -z "$mark" ]]; then | |
mark="${PWD##*/}" | |
read -r -p "Create mark for $mark? (Y/n) " ans | |
if [[ "$ans" == [Nn] ]]; then | |
printf "Mark creation aborted: %s\n" "$mark" 1>&2 | |
return 3 | |
fi | |
fi | |
if [[ -z "$MARKS" ]]; then | |
printf "\$MARKS environment variable not set\n" 1>&2 | |
return 1 | |
fi | |
if [[ ! -d "$MARKS" ]]; then | |
mkdir -p "$MARKS" | |
fi | |
if [[ ! -e "$MARKS/$mark" ]]; then | |
ln -s "$PWD" "$MARKS/$mark" | |
else | |
printf "Mark already exists: %s\n" "$mark" 1>&2 | |
return 2 | |
fi | |
} | |
unmark() { | |
if [[ -z "$1" || -z "$MARKS" ]]; then | |
printf "Usage: unmark MARK\n" 1>&2 | |
return 1 | |
fi | |
if [[ -e "$MARKS/$1" ]]; then | |
rm -f "$MARKS/$1" | |
else | |
printf "Mark does not exist: %s\n" "$1" 1>&2 | |
return 2 | |
fi | |
} | |
marks() { | |
find "$MARKS" -maxdepth 1 -type l -printf "%f -> %l\n" | column -t | |
} | |
_marks_complete() { | |
local word=${COMP_WORDS[COMP_CWORD]} | |
local list=$(find "$MARKS" -maxdepth 1 -type l -printf "%f\n") | |
COMPREPLY=($(compgen -W '${list[@]}' -- "$word")) | |
return 0 | |
} | |
complete -F _marks_complete jump | |
complete -F _marks_complete unmark |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment