Skip to content

Instantly share code, notes, and snippets.

@ljmf00
Last active September 26, 2020 23:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ljmf00/ddf85095997210293eb39482c2ce16bb to your computer and use it in GitHub Desktop.
Save ljmf00/ddf85095997210293eb39482c2ce16bb to your computer and use it in GitHub Desktop.
.bashrc
#!/usr/bin/env bash
# shellcheck shell=bash
# shellcheck disable=SC2034
# shellcheck disable=SC2015
#
# ~/.bashrc
#
# ===============================
# EARLY BOOTSTRAP
# ===============================
# If not running interactively, don't do anything!
[[ $- != *i* ]] && return
# set safe failures
set -euo pipefail
# wait for user input
akey_continue() { read -n 1 -s -r -p "Press any key to continue"; }
trap akey_continue EXIT
# define set_windowtitle and ncolors as early as possible
case "$TERM" in
xterm*|rxvt*|Eterm|aterm|kterm|gnome*|linux*)
set_windowtitle() {
#[[ -n ${DISPLAY+x} ]] && echo -ne "\033]0;$*\007" || :
echo -ne "\033]0;$*\007"
}
# check number of colors available
ncolors="$(tput colors)"
;;
*)
set_windowtitle() {
:
}
ncolors=0
;;
esac
# set window title for initialization
set_windowtitle ".bashrc: init"
# add newline to seperate last session line from tty
[[ "$TERM" == linux* ]] && echo || :
# ===============================
# COLORS DEFINITION
# ===============================
if test -n "$ncolors" && test $ncolors -ge 8; then
fg_bold="\e[1m"
reset_fg_bold="\e[21m"
dim_color="\e[2m"
reset_dim_color="\e[22m"
fg_underlined="\e[4m"
reset_fg_underlined="\e[24m"
blink_color="\e[5m"
reset_blink_color="\e[25m"
reverse_color="\e[7m"
reset_reverse_color="\e[27m"
hidden_color="\e[8m"
reset_hidden_color="\e[28m"
reset_colors="\e[0m"
fg_default_color="\e[39m"
fg_red="\e[31m"
fg_green="\e[32m"
fg_yellow="\e[33m"
fg_cyan="\e[36m"
fg_dark_gray="\e[90m"
fg_light_red="\e[91m"
fg_light_green="\e[92m"
fg_light_magenta="\e[95m"
fg_light_cyan="\e[96m"
# Log levels alias
info="$fg_cyan" # info
warn="$fg_yellow" # yellow
err="$fg_red" # error
else
# dont mark log levels as unbound variables
info='' warn='' err=''
fi
# ===============================
# FUNCTION DEFINITIONS
# ===============================
task-statistics() {
task burndown.monthly
task ghistory
}
sqlplus() {
PRE_TEXT=$(resize |sed -n "1s/COLUMNS=/set linesize /p;2s/LINES=/set pagesize /p"|while read -r line; do printf "%s \ " "$line";done);
if [ -z "$1" ]; then
rlwrap -m -P "$PRE_TEXT" sqlplus /nolog;
else
if ! echo "$1" | grep '^-' > /dev/null; then
rlwrap -m -P "$PRE_TEXT connect $*" sqlplus /nolog;
else
# shellcheck disable=SC2048
# shellcheck disable=SC2086
command sqlplus $*;
fi;
fi
}
last-archnews() {
# The characters "£, §" are used as metacharacters. They should not be encountered in a feed...
# shellcheck disable=SC2001
# shellcheck disable=SC2046
# shellcheck disable=SC2005
echo -e "$(echo $(curl --silent https://www.archlinux.org/feeds/news/ | sed -e ':a;N;$!ba;s/\n/ /g') | \
sed -e 's/&/\&/g
s/&lt;\|&#60;/</g
s/&gt;\|&#62;/>/g
s/<\/a>/£/g
s/href\=\"/§/g
s/<title>/\\n\\n\\n :: \\e[01;31m/g; s/<\/title>/\\e[00m ::\\n/g
s/<link>/ [ \\e[01;36m/g; s/<\/link>/\\e[00m ]/g
s/<description>/\\n\\n\\e[00;37m/g; s/<\/description>/\\e[00m\\n\\n/g
s/<p\( [^>]*\)\?>\|<br\s*\/\?>/\n/g
s/<b\( [^>]*\)\?>\|<strong\( [^>]*\)\?>/\\e[01;30m/g; s/<\/b>\|<\/strong>/\\e[00;37m/g
s/<i\( [^>]*\)\?>\|<em\( [^>]*\)\?>/\\e[41;37m/g; s/<\/i>\|<\/em>/\\e[00;37m/g
s/<u\( [^>]*\)\?>/\\e[4;37m/g; s/<\/u>/\\e[00;37m/g
s/<code\( [^>]*\)\?>/\\e[00m/g; s/<\/code>/\\e[00;37m/g
s/<a[^§|t]*§\([^\"]*\)\"[^>]*>\([^£]*\)[^£]*£/\\e[01;31m\2\\e[00;37m \\e[01;34m[\\e[00;37m \\e[04m\1\\e[00;37m\\e[01;34m ]\\e[00;37m/g
s/<li\( [^>]*\)\?>/\n \\e[01;34m*\\e[00;37m /g
s/<!\[CDATA\[\|\]\]>//g
s/\|>\s*<//g
s/ *<[^>]\+> */ /g
s/[<>£§]//g')\n\n";
}
run-help() { help "$READLINE_LINE" 2>/dev/null || man "$READLINE_LINE"; }
extract () {
if [ -f "$1" ] ; then
case $1 in
*.tar.bz2) tar xvjf "$1" ;;
*.tar.gz) tar xvzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar x "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xvf "$1" ;;
*.tbz2) tar xvjf "$1" ;;
*.tgz) tar xvzf "$1" ;;
*.zip) unzip "$1" ;;
*.Z) uncompress "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "don't know how to extract '$1'..." ;;
esac
else
echo "'$1' is not a valid file!"
fi
}
# Makes new Dir and jumps inside
mcd () { mkdir -p -- "$*" ; cd -- "$*" || exit ; }
# mans: Search manpage given in agument '1' for term given in argument '2' (case insensitive)
# displays paginated result with colored search terms and two lines surrounding each hit.
# Example: mans mplayer codec
mans () { man "$1" | grep -iC2 --color=always "$2" | less ; }
# quiet: mute output of a command
quiet () {
"$*" &> /dev/null &
}
# mkiso: creates iso from current dir in the parent dir (unless defined)
mkiso () {
if type "mkisofs" > /dev/null; then
if [ -z ${1+x} ]; then
local isoname=${PWD##*/}
else
local isoname=$1
fi
if [ -z ${2+x} ]; then
local destpath=../
else
local destpath=$2
fi
if [ -z ${3+x} ]; then
local srcpath=${PWD}
else
local srcpath=$3
fi
if [ ! -f "${destpath}${isoname}.iso" ]; then
echo "writing ${isoname}.iso to ${destpath} from ${srcpath}"
mkisofs -V "${isoname}" -iso-level 3 -r -o "${destpath}${isoname}.iso" "${srcpath}"
else
echo "${destpath}${isoname}.iso already exists"
fi
else
echo "mkisofs cmd does not exist, please install cdrtools"
fi
}
ff () { /usr/bin/find . -name "$@" ; } # ff: Find file under the current directory
# shellcheck disable=SC2145
ffs () { /usr/bin/find . -name "$@"'*' ; } # ffs: Find file whose name starts with a given string
# shellcheck disable=SC2145
ffe () { /usr/bin/find . -name '*'"$@" ; } # ffe: Find file whose name ends with a given string
bigfind() {
if [[ $# -lt 1 ]]; then
echo_warn "Usage: bigfind DIRECTORY"
return
fi
du -a "$1" | sort -n -r | head -n 10
}
# myip: displays your ip address, as seen by the Internet
myip () {
res=$(curl -s checkip.dyndns.org | grep -Eo '[0-9\.]+')
echo -e "Your public IP is: ${fg_green}$res ${reset_colors}"
}
# lsgrep: search through directory contents with grep
# shellcheck disable=SC2010
lsgrep () { ls | grep "$*" ; }
# Logger
# This function log messages
# Usage: log <LEVEL> <MESSAGE>
log_msg2() {
echo -e " $1--> ${fg_dark_gray}$2${reset_colors}"
}
# Fatal logger
# This function log fatal messages
# Usage: fatal <MESSAGE> <EXIT_CODE>
log_fatal_msg2() {
log_msg2 "$err" "$1"
exit "$([ $# -eq 2 ] && echo "$2" || echo 1)"
}
# ===============================
# STARTUP
# ===============================
# block user input
stty -echo
# .bashrc paths
BASHRC_FILE="$HOME/.bashrc"
BASHRC_CHECK="${BASHRC_FILE}.check"
# if this file changed, run checks
if [[ ! -f "$BASHRC_CHECK" || ! "$(cksum "$BASHRC_FILE")" == "$(cat "$BASHRC_CHECK")" ]]; then
NEW_BASHRC=1
# set window title for new file routine
set_windowtitle ".bashrc: check new file"
echo -e "New .bashrc loaded!\n"
# check if ncurses is installed
# check if lolcat is installed
# check if fortune is installed
# check if pygmentize is installed
if ! hash pygmentize 2> /dev/null; then
log_msg2 "$warn" "Package 'pygmentize' is not installed"
#TODO: Try to install package
fi
# check if bash-completion is installed
if [ ! -d /usr/share/bash-completion ]; then
log_msg2 "$warn" "Package 'bash-completion' is not installed"
#TODO: Try to install package
fi
# check if git is installed
if ! hash git 2> /dev/null; then
log_msg2 "$warn" "Package 'git' is not installed"
#TODO: Try to install package
fi
# check if ssh is installed
if ! hash ssh 2> /dev/null; then
log_msg2 "$warn" "Package 'openssh' is not installed"
#TODO: Try to install package
fi
# check if thefuck is installed
if ! hash thefuck 2> /dev/null; then
log_msg2 "$warn" "Package 'thefuck' is not installed"
#TODO: Try to install package
fi
if ! hash micro 2> /dev/null; then
log_msg2 "$warn" "Recommended to install micro"
fi
if hash nano 2> /dev/null && [ ! -f ~/.nanorc ]; then
log_msg2 "$info" "Creating a .nanorc file to support colors"
touch "$HOME/.nanorc"
# adding every supported language at the
find /usr/share/nano/ -iname "*.nanorc" -exec echo include {} \; >> ~/.nanorc
fi
log_msg2 "$info" "Calculating checksum for .bashrc"
cksum "$BASHRC_FILE" > "$BASHRC_CHECK"
# add a final newline to the log
echo
# set window title to warn user
set_windowtitle ".bashrc: waiting for user input"
# wait for user input
akey_continue
# clear entire screen
printf "\033c"
fi
# Bash completion
# shellcheck disable=SC1091
[ -r /usr/share/bash-completion/bash_completion ] && . /usr/share/bash-completion/bash_completion
# pkgfile hook
# shellcheck disable=SC1091
source /usr/share/doc/pkgfile/command-not-found.bash
# ===============================
# BASH OPTIONS
# ===============================
# Bash won't get SIGWINCH if another process is in the foreground.
# Enable checkwinsize so that bash will check the terminal size when
# it regains control.
# http://cnswww.cns.cwru.edu/~chet/bash/FAQ (E11)
#[[ -n ${DISPLAY+x} ]] && shopt -s checkwinsize || :
shopt -s checkwinsize
# Enable history appending instead of overwriting.
shopt -s histappend
# ingore duplicate entries on the history
export HISTCONTROL="ignoredups,ignorespace"
# Enable autocd, when no cd is provided witha valid path
shopt -s autocd 2> /dev/null
# Correct spelling errors during tab-completion
shopt -s dirspell 2> /dev/null
# Correct spelling errors in arguments supplied to cd
shopt -s cdspell 2> /dev/null
# Ignored EOF action on interactive shell
# Value of consecutive EOF characters before exiting
export IGNOREEOF=10
# Automatically trim long paths in the prompt (requires Bash 4.x)
PROMPT_DIRTRIM=2
# Turn on recursive globbing (enables ** to recurse all directories)
shopt -s globstar 2> /dev/null
# Case-insensitive globbing (used in pathname expansion)
shopt -s nocaseglob;
# ===============================
# CONFIGURATIONS
# ===============================
if test -n "$ncolors" && test $ncolors -ge 8; then
# add termcap for less when colors are available
export LESS=-R
export LESS_TERMCAP_mb=$'\E[1;31m' # begin blink
export LESS_TERMCAP_md=$'\E[1;36m' # begin bold
export LESS_TERMCAP_me=$'\E[0m' # reset bold/blink
export LESS_TERMCAP_so=$'\E[01;44;33m' # begin reverse video
export LESS_TERMCAP_se=$'\E[0m' # reset reverse video
export LESS_TERMCAP_us=$'\E[1;32m' # begin underline
export LESS_TERMCAP_ue=$'\E[0m' # reset underline
fi
# Editor configuration
if hash micro 2> /dev/null && test -n "$ncolors" && test $ncolors -ge 8; then
EDITOR='micro'
VISUAL='micro'
# fallback to nano text editor
elif hash nano 2> /dev/null; then
EDITOR='nano'
VISUAL='nano'
# fallback to nvim
elif hash nvim 2> /dev/null; then
EDITOR='nvim'
VISUAL='nvim'
# fallback to vim
elif hash vim 2> /dev/null; then
EDITOR='vim'
VISUAL='vim'
# fallback to vi if none found
else
if ! hash vi 2> /dev/null; then
log_msg2 "$warn" "Falling to 'vi' as default editor but not installed!"
fi
EDITOR='vi'
VISUAL='vi'
fi
export EDITOR
export VISUAL
if hash thefuck 2> /dev/null; then
eval "$(thefuck --alias)"
fi
GPG_TTY="$(tty)"
export GPG_TTY
# SSH Agent
if ! pgrep -u "$USER" ssh-agent > /dev/null; then
ssh-agent > "$HOME/.ssh-agent-thing"
fi
if [ -z ${SSH_AGENT_PID+x} ]; then
# shellcheck disable=SC1090
source "$HOME/.ssh-agent-thing" > /dev/null
fi
# ccache configurations
export USE_CCACHE=1
export CCACHE_COMPRESS=1
# Wine
export WINEPREFIX=$HOME/.wine
export WINEARCH=win64
# Java
JAVA_HOME="/usr/lib/jvm/$(archlinux-java get)/"
export JAVA_HOME
# Android
export ANDROID_HOME=/opt/android-sdk
# Kitty
if [ "$TERM" == "xterm-kitty" ]; then
# shellcheck disable=SC1090
source <(kitty + complete setup bash)
alias ssh='kitty kitten ssh'
fi
# ===============================
# BINDS
# ===============================
# up and down keybinds
bind '"\e[A": history-search-backward'
bind '"\e[B": history-search-forward'
# CTRL + left and right keybinds
bind '"\e[1;5C": forward-word'
bind '"\e[1;5D": backward-word'
# Page up and page down completion navigation
bind '"\e[6~": menu-complete'
bind '"\e[5~": menu-complete-backward'
# partially complete the word and show all possible completions if it is still ambiguous
bind 'set show-all-if-ambiguous on'
# completion will ignore case when doing partial completion
bind 'set completion-ignore-case on'
# The double tab will be changed to a single tab when unmodified
bind 'set show-all-if-unmodified on'
# Treat hyphens and underscores as equivalent
bind "set completion-map-case on"
# Immediately add a trailing slash when autocompleting symlinks to directories
bind "set mark-symlinked-directories on"
# Color files by types
bind 'set colored-stats On'
# Append char to indicate type
bind 'set visible-stats On'
# Mark symlinked directories
bind 'set mark-symlinked-directories On'
# Color the common prefix
bind 'set colored-completion-prefix On'
# Color the common prefix in menu-complete
bind 'set menu-complete-display-prefix On'
# run help
bind -m vi-insert -x '"\eh": run-help'
bind -m emacs -x '"\eh": run-help'
# Enable history expansion with space
# E.g. typing !!<space> will replace the !! with your last command
bind Space:magic-space
# dont trigger bell when TAB
bind 'set bell-style none'
# from /etc/inputrc
bind 'set meta-flag on'
bind 'set input-meta on'
bind 'set convert-meta off'
bind 'set output-meta on'
# ===============================
# ALIAS
# ===============================
# Common directories functions
alias ...='../..'
alias ....='../../..'
alias .....='../../../..'
alias ......='../../../../..'
alias cd..='cd ..' # Go back 1 directory level (for fast typers)
alias .3='cd ../../..' # Go back 3 directory levels
alias .4='cd ../../../..' # Go back 4 directory levels
alias .5='cd ../../../../..' # Go back 5 directory levels
alias .6='cd ../../../../../..' # Go back 6 directory levels
alias -- -='cd -'
alias 1='cd -'
alias 2='cd -2'
alias 3='cd -3'
alias 4='cd -4'
alias 5='cd -5'
alias 6='cd -6'
alias 7='cd -7'
alias 8='cd -8'
alias 9='cd -9'
# Directory management
alias md='mkdir -p'
alias rd='rmdir'
# List directory contents
alias l='ls -lah'
alias la='ls -lAh'
alias ll='ls -lh'
alias lsa='ls -lah'
# Git
alias glg='git log --stat'
alias glgg='git log --graph'
alias glgga='git log --graph --decorate --all'
alias glgm='git log --graph --max-count=10'
alias glgp='git log --stat -p'
alias glo='git log --oneline --decorate'
alias glod='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset'\'
alias glods='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset'\'' --date=short'
alias glog='git log --oneline --decorate --graph'
alias gloga='git log --oneline --decorate --graph --all'
alias glol='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'
alias glola='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --all'
alias glols='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --stat'
alias gra='git remote add'
alias grb='git rebase'
alias grba='git rebase --abort'
alias grbc='git rebase --continue'
alias grbs='git rebase --skip'
alias grev='git revert'
alias grh='git reset'
alias grhh='git reset --hard'
alias grm='git rm'
alias grmc='git rm --cached'
alias grmv='git remote rename'
alias groh='git reset origin/$(git_current_branch) --hard'
alias grrm='git remote remove'
alias grs='git restore'
alias grset='git remote set-url'
alias grup='git remote update'
alias grv='git remote -v'
alias gsh='git show'
alias gsi='git submodule init'
alias gsps='git show --pretty=short --show-signature'
alias gss='git status -s'
alias gst='git status'
alias gsta='git stash push'
alias gstc='git stash clear'
alias gstd='git stash drop'
alias gstl='git stash list'
alias gstp='git stash pop'
alias gswc='git switch -c'
# Visual Studio Code
alias vsc='code-oss .'
alias vsca='code-oss --add'
alias vscd='code-oss --diff'
alias vscde='code-oss --disable-extensions'
alias vsced='code-oss --extensions-dir'
alias vscg='code-oss --goto'
alias vscie='code-oss --install-extension'
alias vscl='code-oss --log'
alias vscn='code-oss --new-window'
alias vscr='code-oss --reuse-window'
alias vscu='code-oss --user-data-dir'
alias vscue='code-oss --uninstall-extension'
alias vscv='code-oss --verbose'
alias vscw='code-oss --wait'
# Misc
alias waka='npx waka'
alias v='nvim'
alias bashconfig='$EDITOR ~/.bashrc'
alias cls='printf "\033c"'
alias ccat='pygmentize -g'
alias lccat='pygmentize -g -O style=colorful,linenos=1'
# Improved commands
if test -n "$ncolors" && test $ncolors -ge 8; then
alias grep='grep --color=auto --exclude-dir={.bzr,CVS,.git,.hg,.svn,.idea,.tox}'
alias ls='ls --color=tty'
alias ip='ip -color=auto'
alias dmesg='dmesg --color=always'
else
alias grep='grep --exclude-dir={.bzr,CVS,.git,.hg,.svn,.idea,.tox}'
fi
# Fancy dir alias
function d () {
if [[ -n $1 ]]; then
dirs "$@"
else
dirs -v | head -10
fi
}
# ===============================
# GOTO COMPLETION COMMAND
# ===============================
# Under MIT License
#
# Copyright (c) 2018 Lazarus Lazaridis
# Copyright (c) 2020 Luís Ferreira
# Changes to the given alias directory
# or executes a command based on the arguments.
goto()
{
local target
_goto_resolve_db
if [ -z "$1" ]; then
# display usage and exit when no args
_goto_usage
return
fi
subcommand="$1"
shift
case "$subcommand" in
-c|--cleanup)
_goto_cleanup "$@"
;;
-r|--register) # Register an alias
_goto_register_alias "$@"
;;
-u|--unregister) # Unregister an alias
_goto_unregister_alias "$@"
;;
-p|--push) # Push the current directory onto the pushd stack, then goto
_goto_directory_push "$@"
;;
-o|--pop) # Pop the top directory off of the pushd stack, then change that directory
_goto_directory_pop
;;
-l|--list)
_goto_list_aliases
;;
-x|--expand) # Expand an alias
_goto_expand_alias "$@"
;;
-h|--help)
_goto_usage
;;
-v|--version)
_goto_version
;;
*)
_goto_directory "$subcommand"
;;
esac
return $?
}
_goto_resolve_db()
{
local CONFIG_DEFAULT="${XDG_CONFIG_HOME:-$HOME/.config}/goto"
GOTO_DB="${GOTO_DB:-$CONFIG_DEFAULT}"
touch -a "$GOTO_DB"
}
_goto_usage()
{
cat <<\USAGE
usage: goto [<option>] <alias> [<directory>]
default usage:
goto <alias> - changes to the directory registered for the given alias
OPTIONS:
-r, --register: registers an alias
goto -r|--register <alias> <directory>
-u, --unregister: unregisters an alias
goto -u|--unregister <alias>
-p, --push: pushes the current directory onto the stack, then performs goto
goto -p|--push <alias>
-o, --pop: pops the top directory from the stack, then changes to that directory
goto -o|--pop
-l, --list: lists aliases
goto -l|--list
-x, --expand: expands an alias
goto -x|--expand <alias>
-c, --cleanup: cleans up non existent directory aliases
goto -c|--cleanup
-h, --help: prints this help
goto -h|--help
-v, --version: displays the version of the goto script
goto -v|--version
USAGE
}
# Displays version
_goto_version()
{
echo "goto version 2.0.0"
}
# Expands directory.
# Helpful for ~, ., .. paths
_goto_expand_directory()
{
builtin cd "$1" 2>/dev/null && pwd
}
# Lists registered aliases.
_goto_list_aliases()
{
local IFS=$' '
if [ -f "$GOTO_DB" ]; then
while read -r name directory; do
printf '\e[1;36m%20s \e[0m%s\n' "$name" "$directory"
done < "$GOTO_DB"
else
echo "You haven't configured any directory aliases yet."
fi
}
# Expands a registered alias.
_goto_expand_alias()
{
if [ "$#" -ne "1" ]; then
_goto_error "usage: goto -x|--expand <alias>"
return
fi
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -z "$resolved" ]; then
_goto_error "alias '$1' does not exist"
return
fi
echo "$resolved"
}
# Lists duplicate directory aliases
_goto_find_duplicate()
{
local duplicates=
duplicates=$(sed -n 's:[^ ]* '"$1"'$:&:p' "$GOTO_DB" 2>/dev/null)
echo "$duplicates"
}
# Registers and alias.
_goto_register_alias()
{
if [ "$#" -ne "2" ]; then
_goto_error "usage: goto -r|--register <alias> <directory>"
return 1
fi
if ! [[ $1 =~ ^[[:alnum:]]+[a-zA-Z0-9_-]*$ ]]; then
_goto_error "invalid alias - can start with letters or digits followed by letters, digits, hyphens or underscores"
return 1
fi
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -n "$resolved" ]; then
_goto_error "alias '$1' exists"
return 1
fi
local directory
directory=$(_goto_expand_directory "$2")
if [ -z "$directory" ]; then
_goto_error "failed to register '$1' to '$2' - can't cd to directory"
return 1
fi
local duplicate
duplicate=$(_goto_find_duplicate "$directory")
if [ -n "$duplicate" ]; then
_goto_warning "duplicate alias(es) found: \\n$duplicate"
fi
# Append entry to file.
echo "$1 $directory" >> "$GOTO_DB"
echo "Alias '$1' registered successfully."
}
# Unregisters the given alias.
_goto_unregister_alias()
{
if [ "$#" -ne "1" ]; then
_goto_error "usage: goto -u|--unregister <alias>"
return 1
fi
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -z "$resolved" ]; then
_goto_error "alias '$1' does not exist"
return 1
fi
# shellcheck disable=SC2034
local readonly GOTO_DB_TMP="$HOME/.goto_"
# Delete entry from file.
sed "/^$1 /d" "$GOTO_DB" > "$GOTO_DB_TMP" && mv "$GOTO_DB_TMP" "$GOTO_DB"
echo "Alias '$1' unregistered successfully."
}
# Pushes the current directory onto the stack, then goto
_goto_directory_push()
{
if [ "$#" -ne "1" ]; then
_goto_error "usage: goto -p|--push <alias>"
return
fi
{ pushd . || return; } 1>/dev/null 2>&1
_goto_directory "$@"
}
# Pops the top directory from the stack, then goto
_goto_directory_pop()
{
{ popd || return; } 1>/dev/null 2>&1
}
# Unregisters aliases whose directories no longer exist.
_goto_cleanup()
{
if ! [ -f "$GOTO_DB" ]; then
return
fi
while IFS= read -r i && [ -n "$i" ]; do
echo "Cleaning up: $i"
_goto_unregister_alias "$i"
done <<< "$(awk '{al=$1; $1=""; dir=substr($0,2);
system("[ ! -d \"" dir "\" ] && echo " al)}' "$GOTO_DB")"
}
# Changes to the given alias' directory
_goto_directory()
{
local target
target=$(_goto_resolve_alias "$1") || return 1
builtin cd "$target" 2> /dev/null || \
{ _goto_error "Failed to goto '$target'" && return 1; }
}
# Fetches the alias directory.
_goto_find_alias_directory()
{
local resolved
resolved=$(sed -n "s/^$1 \\(.*\\)/\\1/p" "$GOTO_DB" 2>/dev/null)
echo "$resolved"
}
# Displays the given error.
# Used for common error output.
_goto_error()
{
(>&2 echo -e "goto error: $1")
}
# Displays the given warning.
# Used for common warning output.
_goto_warning()
{
(>&2 echo -e "goto warning: $1")
}
# Displays entries with aliases starting as the given one.
_goto_print_similar()
{
local similar
similar=$(sed -n "/^$1[^ ]* .*/p" "$GOTO_DB" 2>/dev/null)
if [ -n "$similar" ]; then
(>&2 echo "Did you mean:")
(>&2 column -t <<< "$similar")
fi
}
# Fetches alias directory, errors if it doesn't exist.
_goto_resolve_alias()
{
local resolved
resolved=$(_goto_find_alias_directory "$1")
if [ -z "$resolved" ]; then
_goto_error "unregistered alias $1"
_goto_print_similar "$1"
return 1
else
echo "${resolved}"
fi
}
# Completes the goto function with the available commands
_complete_goto_commands()
{
local IFS=$' \t\n'
# shellcheck disable=SC2207
COMPREPLY=($(compgen -W "-r --register -u --unregister -p --push -o --pop -l --list -x --expand -c --cleanup -v --version" -- "$1"))
}
# Completes the goto function with the available aliases
_complete_goto_aliases()
{
local IFS=$'\n' matches
_goto_resolve_db
# shellcheck disable=SC2207
matches=($(sed -n "/^$1/p" "$GOTO_DB" 2>/dev/null))
if [ "${#matches[@]}" -eq "1" ]; then
# remove the filenames attribute from the completion method
compopt +o filenames 2>/dev/null
# if you find only one alias don't append the directory
COMPREPLY=("${matches[0]// *}")
else
for i in "${!matches[@]}"; do
# remove the filenames attribute from the completion method
compopt +o filenames 2>/dev/null
if ! [[ $(uname -s) =~ Darwin* ]]; then
matches[$i]=$(printf '%*s' "-$COLUMNS" "${matches[$i]}")
COMPREPLY+=("$(compgen -W "${matches[$i]}")")
else
COMPREPLY+=("${matches[$i]// */}")
fi
done
fi
}
# Bash programmable completion for the goto function
_complete_goto_bash()
{
local cur="${COMP_WORDS[$COMP_CWORD]}" prev
if [ "$COMP_CWORD" -eq "1" ]; then
# if we are on the first argument
if [[ $cur == -* ]]; then
# and starts like a command, prompt commands
_complete_goto_commands "$cur"
else
# and doesn't start as a command, prompt aliases
_complete_goto_aliases "$cur"
fi
elif [ "$COMP_CWORD" -eq "2" ]; then
# if we are on the second argument
prev="${COMP_WORDS[1]}"
if [[ $prev = "-u" ]] || [[ $prev = "--unregister" ]]; then
# prompt with aliases if user tries to unregister one
_complete_goto_aliases "$cur"
elif [[ $prev = "-x" ]] || [[ $prev = "--expand" ]]; then
# prompt with aliases if user tries to expand one
_complete_goto_aliases "$cur"
elif [[ $prev = "-p" ]] || [[ $prev = "--push" ]]; then
# prompt with aliases only if user tries to push
_complete_goto_aliases "$cur"
fi
elif [ "$COMP_CWORD" -eq "3" ]; then
# if we are on the third argument
prev="${COMP_WORDS[1]}"
if [[ $prev = "-r" ]] || [[ $prev = "--register" ]]; then
# prompt with directories only if user tries to register an alias
local IFS=$' \t\n'
# shellcheck disable=SC2207
COMPREPLY=($(compgen -d -- "$cur"))
fi
fi
}
# goto aliases
# shellcheck disable=SC2207
goto_aliases=($(alias | sed -n "s/.*\s\(.*\)='goto'/\1/p"))
goto_aliases+=("goto")
# enable completion routines for bash
for alias in "${goto_aliases[@]}"; do
if ! [[ $(uname -s) =~ Darwin* ]]; then
complete -o filenames -F _complete_goto_bash "$alias"
else
complete -F _complete_goto_bash "$alias"
fi
done
# ===============================
# WITH COMPLETION COMMAND
# ===============================
_with_usage()
{
echo -e "Usage: with <prefix>"
}
_with_help()
{
_with_usage
echo -e "\n -h, --help : Display command help"
}
with()
{
#add options here, such as -h, -v
declare -a prefix
prefix=( "$@" )
case ${prefix[*]} in
"" )
echo "Missing arguments."
_with_usage
;;
"-h"|"--help")
_with_help
;;
-*)
echo "Unrecognised option: ${prefix[*]}"
_with_help
;;
esac
pmpt=${prefix[*]}
}
# ===============================
# PROMPT
# ===============================
rprompt_git()
{
local abrev_head dirty_status ret="";
# guess abrev head name
abrev_head="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
if [ "$abrev_head" == "HEAD" ]; then
# on detached branch, give short hash instead
abrev_head="$(git rev-parse --short HEAD 2>/dev/null)"
# maybe no detached branch and no history
# shellcheck disable=SC2181
if [ $? -gt 0 ]; then
abrev_head="$(git branch --show-current 2>/dev/null)"
fi
fi
# check if branch was modified
dirty_status="$([[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]] && echo "*")"
# start branch identifier and dirty status scope
ret+="${fg_bold}${fg_default_color}["
ret+="${fg_light_green}${abrev_head}${dirty_status}"
# close scope and reset colors
ret+="${fg_default_color}]${reset_colors}"
echo -en "$ret"
}
# Print the right prompt
# Please use this to print:
# printf "%*s" $COLUMNS "ATUM"
rightprompt()
{
local printable c_printable
if git rev-parse --git-dir &> /dev/null; then
printable="$(rprompt_git)"
fi
c_printable="$(echo -e "$printable" | sed "s/$(echo -e "\e")[^m]*m//g")"
printf "%*s" $((COLUMNS + ${#printable} - ${#c_printable} )) "$printable"
}
prompt_exitstatus()
{
# save exit code to use later
PROMPT_EXITSTATUS="${?}"
if [[ ${PROMPT_EXITSTATUS} == "0" ]]
then
PROMPT_EXITSTATUS=
else
PROMPT_EXITSTATUS+=" "
fi
}
prompt_command () {
prompt_exitstatus
}
case "$TERM" in
xterm*|rxvt*|Eterm|aterm|kterm|gnome*|linux*)
PROMPT_COMMAND=prompt_command
export PROMPT_COMMAND
# right-side definition
PS1="\\[\$(tput sc; rightprompt; tput rc)\\]"
# left-side definition
# set window title
if [[ -n ${SSH_CLIENT+x} || -n ${SSH_TTY+x} ]]; then
PS1+='\[\e]2;[ssh] \u@\h:\w\a\]'
else
PS1+='\[\e]2;\u@\h:\w\a\]'
fi
# start bold
PS1+="\\[${fg_bold}\\]"
# check for root user
if [ "$EUID" -eq 0 ]; then
PS1+="\\[${fg_red}\\]"
else
PS1+="\\[${fg_light_cyan}\\]"
fi
# location info
PS1+="\\u@\\h \\[${fg_light_green}\\]\\W "
# exit status handeling
PS1+="\\[${fg_light_red}\\]\$PROMPT_EXITSTATUS"
# final arrow
PS1+="\\[${fg_light_magenta}\\]►\\[${reset_colors}\\] "
export PS1
export PS2="\\[${fg_light_magenta}\\]►\\[$reset_colors\\] "
#\\[\$(tput sc; echo -en "${fg_light_red}↵$reset_colors"; tput rc)\\]
;;
*)
# Default prompt PS1
PS1='[\u@\h \W]\$ '
export PS1
;;
esac
if [[ "$TERM" == screen* ]]; then
export PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND; }'printf "\033_%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"'
elif [[ "$TERM" == linux* ]]; then
# set terminal cursor on tty
export PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND; }'tput cnorm'
fi
# ===============================
# GREETINGS
# ===============================
# greetings only on normal users
if [ "$EUID" -ne 0 ]; then
NUMBER_OF_SESSIONS="$(who | wc -l)"
NUMOF_ONLINE_NETWORKS=0
# shellcheck disable=SC2010
for interface in $(ls /sys/class/net/ | grep -v lo); do
if [[ "$(cat "/sys/class/net/$interface/carrier")" == 1 ]]; then
NUMOF_ONLINE_NETWORKS=$((NUMOF_ONLINE_NETWORKS+1))
fi
done
echo " Welcome,"
figlets_arr=('standard' 'big' 'slant' 'small' 'banner' 'mini' 'smslant' 'script' 'smscript' 'shadow' 'smshadow')
GREETINGS_HEADER="$( if hash figlet 2>/dev/null; then
figlet -f "${figlets_arr[$((RANDOM % ${#figlets_arr[@]}))]}" lsferreira
else
# lsferreira header
cat << EOF
┬ ┌─┐┌─┐┌─┐┬─┐┬─┐┌─┐┬┬─┐┌─┐
│ └─┐├┤ ├┤ ├┬┘├┬┘├┤ │├┬┘├─┤
┴─┘└─┘└ └─┘┴└─┴└─└─┘┴┴└─┴ ┴
EOF
fi )"
# pride month with lolcat
if hash lolcat 2>/dev/null && [ "$(date +%m)" == "06" ]; then
echo -e "$GREETINGS_HEADER\n\n It's pride month!\n" | lolcat
else
echo -e "$GREETINGS_HEADER"
fi
# last logged session
{ [[ "$TERM" != linux* ]] || [[ "$TERM" == linux* && -n ${NEW_BASHRC+x} ]]; } \
&& echo -e " Last Session: $(last -1 -R "$USER" -n 1 | head -1 |cut -c 23-38)" \
|| :
# print number of active sessions if greater than 1
if [ "$NUMBER_OF_SESSIONS" -gt 1 ]; then
echo -e " There's currently $NUMBER_OF_SESSIONS active sessions."
fi
# reminder for no network connection
if [ "$NUMOF_ONLINE_NETWORKS" -eq 0 ]; then
echo -e " You're not connected to any network."
fi
# Write random quote
if hash fortune 2> /dev/null; then
printf '\n%s\n' "$(fortune)"
fi
fi # [ "$EUID" -ne 0 ]
# ===============================
# FINAL BOOTSTRAP
# ===============================
# reenable input
stty echo
# reenable bash failures
set +euo pipefail
# untrap exit
trap - EXIT
# trap hide cursor on tty exit
[[ "$TERM" == linux* ]] && trap 'tput civis' EXIT || :
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment