Last active
September 26, 2020 23:20
-
-
Save ljmf00/ddf85095997210293eb39482c2ce16bb to your computer and use it in GitHub Desktop.
.bashrc
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
#!/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/<\|</</g | |
s/>\|>/>/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