@@ -1,5 +1,9 @@
#! /bin/bash
# #### DEBUG ONLY #####
# set -v
# #####################
# Localization
export LANG=' en_US.UTF-8'
export LC_COLLATE=' en_US.UTF-8'
@@ -42,7 +46,7 @@ export PS1='\['"${NORMAL}${GREEN_COLOR}"'\]\u\['"${NORMAL}"'\]@\H:\['"${BOLD}${R
export PS2=' > '
# Editor
export EDITOR=' /usr/bin/nano'
export EDITOR=' /usr/local/ bin/nano'
# Python startup
export PYTHONSTARTUP=" ${HOME} /.pythonrc.py"
@@ -54,23 +58,31 @@ export HISTSIZE=1048576
# History tuning
export HISTCONTROL=" ignoredups"
# Saves history to HISTFILE ASAP
export PROMPT_COMMAND=" history -a; ${PROMPT_COMMAND} "
# Shell options
shopt -s autocd # ./dir <=> cd dir
shopt -s cdspell # autocorrection
shopt -s checkwinsize # always have actual $LINES & $COLUMNS
shopt -u cmdhist # use semicolon instead of newline
shopt -u direxpand # do not expand tilde and so on
shopt -s dotglob # echo * sees dotfiles
shopt -s extdebug # MOAR debugging
shopt -s globstar # '**' support
shopt -u gnu_errfmt # POSIX, not GNU error messages
shopt -s histappend # Appends history to $HISTFILE instead of overwriting
shopt -s huponexit # kill all bg jobs on exit
shopt -u lithist # use semicolons instead of newlines in history
shopt -s autocd # ./dir <=> cd dir
shopt -s cdspell # autocorrection
shopt -s checkwinsize # always have actual $LINES & $COLUMNS
shopt -u cmdhist # use semicolon instead of newline
shopt -u direxpand # do not expand tilde and so on
shopt -s dotglob # echo * sees dotfiles
shopt -s extdebug # MOAR debugging
shopt -s globstar # '**' support
shopt -u gnu_errfmt # POSIX, not GNU error messages
shopt -s histappend # Appends history to $HISTFILE instead of overwriting
shopt -s huponexit # kill all bg jobs on exit
shopt -u lithist # use semicolons instead of newlines in history
shopt -s no_empty_cmd_completion # empty command completion is stupid
shopt -u nocaseglob # filenames ARE case-sensitive
shopt -u nocasematch # comparisons ARE case-sensitive
shopt -s xpg_echo # echo has '-e' by default
shopt -u nocaseglob # filenames ARE case-sensitive
shopt -u nocasematch # comparisons ARE case-sensitive
shopt -s xpg_echo # echo has '-e' by default
# bash-completion
if [ -f /usr/local/etc/bash_completion.sh ]; then
. /usr/local/etc/bash_completion.sh
fi
# Reloads bash environment
r () {
@@ -86,7 +98,7 @@ update_title() {
expand_path () {
local old_ifs=" ${IFS} "
local new_path=" ${PATH} "
IFS=' :'
new_dirs=( " $@ " )
for dirs_string in " ${new_dirs[@]} " ; do
@@ -102,74 +114,231 @@ expand_path() {
}
# Path
expand_path " /opt/local/libexec/gnubin" # GNU Coreutils from MacPorts
expand_path " /opt/local/bin:/opt/local/sbin" # MacPorts binaries
expand_path " ~/local/bin" # some stuff
expand_path " ~/adt-bundle-mac-x86_64/sdk/tools:~/adt-bundle-mac-x86_64/sdk/platform-tools" # Android SDK
expand_path " /usr/local/opt/coreutils/libexec/gnubin" # Homebrew
MANPATH=" /usr/local/opt/coreutils/libexec/gnuman:$MANPATH "
# Thick black horizontal line
hr () {
local cols=$(( COLUMNS + 0 )) # cols to int
echo -ne " ${BLACK_COLOR}${BLACK_BG_COLOR} "
if [ ${cols} -gt 0 ]; then
python -c ' print " " * ' $(( cols))
else
echo -ne ' \x1b\n'
fi
echo -ne " ${NORMAL} "
}
# Alias template
__alias_func_template () {
local orig_binary=" $( which ${cmd} ) "
echo ' OLDPATH="${PATH}"'
echo ' unset PATH'
__random_ext () {
local extChars=" abcdefghijklmnopqrstuvwxyz1234567890"
local result=" "
for i in $( seq 1 10) ; do
result=" ${result}${extChars: $(($RANDOM % ${# extChars} )): 1} "
done
echo " ${result} "
}
__backup_command () {
local cmd=" $1 "
local cmdType=" $( type -t " ${cmd} " ) "
if [ " $? " -ne 0 ]; then
echo " __backup_command: ${cmd} : command not found" >&2
return 1
fi
local backupCmd=" "
while : ; do
backupCmd=" __${cmd} _backup_$( __random_ext) "
if ! type -t " ${backupCmd} " > /dev/null; then
break
fi
done
case " ${cmdType} " in
" alias" )
eval " $( alias " ${cmd} " | sed " s/^alias ${cmd} =/alias ${backupCmd} =/" ) "
unalias " ${cmd} "
;;
" keyword" )
echo " __backup_command: ${cmd} : is shell keyword" >&2
return 1
;;
" function" )
eval " ${backupCmd} () { $( declare -f ${cmd} | tail -n +2) }"
unset -f " ${cmd} "
;;
" builtin" )
backupCmd=" builtin ${cmd} "
;;
" file" )
backupCmd=" $( which ${cmd} ) "
;;
esac
echo " ${backupCmd} "
}
__extend_command_func () {
local cmd=" ${1} "
shift
local ORIG_CMD=" $( __backup_command " ${cmd} " ) "
if [ " $? " -ne 0 ]; then
return 1
fi
local funcName=" ${1} "
shift
local funcArgs=" $@ "
echo " ${cmd} " ' () {'
echo ' local orig_' " ${cmd} " ' ="' " ${orig_binary} " ' "'
echo ' local disable_ref="__DISABLE_' ${cmd^^* } ' _ALIAS"'
echo ' local disable_ref2="__DISABLE_' ${cmd^^* } ' _MACRO"'
echo ' local src_cmd="${orig_' ${cmd} ' }"'
echo ' if [ -z "${!disable_ref}" ] && [ -z "${!disable_ref2}" ]; then'
echo ' "${src_cmd}"' ${addn_pre_args} ' "$@"' ${addn_post_args}
echo ' else'
echo ' "${src_cmd}" "$@"'
echo ' fi'
echo ' if type -t "${__DISABLE_' " ${cmd^^} " ' _MACRO}" >/dev/null || type -t "${__DISABLE_' " ${cmd^^} " ' _ALIAS}" >/dev/null; then'
echo ' ' " ${ORIG_CMD} " ' "$@"'
echo ' else'
eval " ${funcName} ${funcArgs} "
echo ' fi'
echo ' }'
echo ' export PATH="${OLDPATH}"'
echo ' unset OLDPATH'
}
# Wrapper for alias_func_template
__set_default_args () {
local cmd=" $1 "
__extend_command_args_helper () {
local cmd=" ${1} "
shift
local addn_pre_args=" $* "
local code=" $( __alias_func_template) "
eval " ${code} "
echo -E " ${ORIG_CMD} " " $@ " ' "$@"'
}
__extend_command_args () {
local cmd=" ${1} "
__extend_command_func " ${cmd} " ' __extend_command_args_helper' " $@ "
}
__extend_command_alias_helper () {
local aliasBase=" ${1} "
shift
echo -E " ${aliasBase} " " $@ " ' "$@"'
}
__extend_command_alias () {
local cmd=" ${1} "
shift
local aliasBase=" ${1} "
shift
__extend_command_func " ${cmd} " ' __extend_command_alias_helper' " ${aliasBase} " " $@ "
}
# Overrides default command behaviour if possible
# Usage: extend_command <command> <options>
# Options: -t [func|args] Extension type.
# `func' replaces the command implementation with evaluated output
# value of function(s) passed after this option. ${ORIG_CMD} is
# replaced with backuped original command call.
# `alias` works exactly as defining a shell alias for the command
# `args' extension type just adds supplied arguments to original
# command implementation. This type is default and is used when no
# -t option is supplied.
extend_command () {
local cmd=" ${1} "
shift
local endOfOptions=" no"
local extendFunc=" __extend_command_args"
while [ " ${endOfOptions} " == " no" ]; do
local arg=" ${1} "
case " ${arg} " in
" -t" )
local extType=" ${2} "
case " ${extType} " in
" func" )
extendFunc=" __extend_command_func"
;;
" alias" )
extendFunc=" __extend_command_alias"
;;
esac
shift 2
;;
* )
endOfOptions=" yes"
;;
esac
done
# echo "$(${extendFunc} ${cmd} $@)"
eval " $( ${extendFunc} ${cmd} $@ ) "
}
# Colored output for ls & grep
__set_default_args ls --color=auto -la
__set_default_args grep --color=auto
__set_default_args egrep --color=auto
__set_default_args fgrep --color=auto
__set_default_args zgrep --color=auto
__set_default_args zegrep --color=auto
__set_default_args zfgrep --color=auto
__set_default_args xargs -d ' \\\\n' # Separate args on newline only
__set_default_args nano -c # Line numbers
__set_default_args diff -ru # Unified and recursive diff
if ! which gedit > /dev/null 2> /dev/null; then
# Override gedit only if it's not in $PATH (OSX)
gedit () {
open -a textwrangler " $@ "
}
fi
extend_command ls --color=auto -la
extend_command grep --color=auto
extend_command egrep --color=auto
extend_command fgrep --color=auto
extend_command zgrep --color=auto
extend_command zegrep --color=auto
extend_command zfgrep --color=auto
extend_command nano -c # Line numbers
extend_command diff -ru # Unified and recursive diff
extend_command xargs -d ' "\\n"' # Sets arguments separator to newline instead of any whitespace
__gedit_impl () {
for f in " $@ " ; do
[ ! -e " ${f} " ] && touch " ${f} "
[ -e " ${f} " ] && open -a textwrangler " ${f} "
done
}
extend_command gedit -t alias __gedit_impl
extend_command edit -t alias __gedit_impl
extend_command beep -t alias afplay ' /System/Library/Sounds/Glass.aiff'
__LESS_HOOKS=( check_plist )
__less_hook_check_plist () {
local fileType=" ${1} "
if [ " $fileType " == " Apple binary property list" ]; then
echo " __less_binary_plist"
fi
}
__less_binary_plist () {
local lessFile=" ${1} "
shift
plutil -convert xml1 -o - " ${lessFile} " | less " $@ "
}
__less_check_hooks () {
echo ' local lessFile="$1"'
echo ' echo "${lessFile}" >&2'
echo ' local hookFunc=""'
echo ' if [ -r "${lessFile}" ]; then'
echo ' local fileType="$(file -b "${lessFile}")"'
echo ' for hook in ' " ${__LESS_HOOKS[@]} " ' ; do'
echo ' hookFunc="$(__less_hook_${hook} "${fileType}")"'
echo ' if [ ! -z "${hookFunc}" ]; then'
echo ' break'
echo ' fi'
echo ' done'
echo ' fi'
echo ' if [ -z "${hookFunc}" ]; then'
echo ' ' " ${ORIG_CMD} " ' "$@"'
echo ' else'
echo ' ${hookFunc} "$@"'
echo ' fi'
}
extend_command less -t func __less_check_hooks # Adds some input filters for `less' command
# UNIX timestamp -> human-readable date
timestamp2date () {
@@ -201,7 +370,7 @@ pretty_date() {
# Pseudographic version of GitHub's Network pane
git_branches () {
git log --graph --full-history --all --pretty=format:" %Cred%h%Creset%x09%ct%x09%Cgreen%d%Creset%x09%s"
git log --graph --full-history --all --pretty=format:" %Cred%h%Creset%x09%ct%x09%Cgreen%d%Creset%x09%s"
# TODO: datetime update
# | awk '{printf "%s\t%s\t", $1, $2; system ("echo pretty_date $3"); for (i = 4; i <= NF; i++) {printf "%s ", $i}; printf "\n"}'
}
@@ -211,7 +380,7 @@ __pyurlalias_template() {
arg=" $1 "
local name=" ${arg%% =* } "
local func=" ${arg#* =} "
echo " ${name} " ' () {'
echo ' python -c "import sys, urllib; print urllib.' " ${func} " ' (sys.stdin.read ())" "$@"'
echo ' }'
@@ -233,7 +402,7 @@ __pyurlalias urldecode_p=unquote_plus
noize () {
local beeps=" ${1:- 5} "
local delay=" ${2:- 0.1} "
for i in $( seq 1 " ${beeps} " ) ; do
echo -ne ' \a'
sleep " ${delay} "
@@ -259,14 +428,14 @@ git_conflicts() {
_git_srv () {
local srv=" $1 "
shift
local src=" $1 "
shift
local action=" $1 "
shift
local repo=" $1 "
shift
local src=" $1 "
shift
local action=" $1 "
shift
local repo=" $1 "
shift
git " ${action} " " ${srv} /${src} /${repo} " " $@ "
git " ${action} " " ${srv} /${src} /${repo} " " $@ "
}
# _github [source] [action] [repo] [...]
@@ -325,14 +494,18 @@ EOF
}
# Cross-platform way of getting real file path
py_realpath () {
python -c ' import os, sys; print "\n".join ([os.path.realpath (p) for p in sys.argv [1:]])' " $@ "
__setup_realpath () {
if ! which realpath & > /dev/null; then
local py_realpath_src=$( cat << EOF
realpath() {
python -c 'import os, sys; print "\n".join ([os.path.realpath (p) for p in sys.argv [1:]])' "$@ "
}
EOF
)
eval " ${py_realpath_src} "
fi
}
# bash-completion
if [ -f /opt/local/etc/profile.d/bash_completion.sh ]; then
. /opt/local/etc/profile.d/bash_completion.sh
fi
__setup_realpath
# Prints out shell running time
shell_uptime () {
@@ -347,34 +520,265 @@ shell_uptime() {
# Finds mentions of argument in Bash history file
hgrep () {
local pattern=" $1 "
grep " ${pattern} " ~ /.bash_history
grep " ${pattern} " " ${HISTFILE} "
}
# Automatically adds echo "\n"; to every php -r call
__set_php_linewrap () {
local cmd=" php"
local addn_post_args=" ; echo"
local code=" $( __alias_func_template) "
eval " ${code} "
__php_linewrap () {
echo " ${ORIG_CMD} " ' "$@"'
echo ' echo'
}
__set_php_linewrap
extend_command php -t func ' __php_linewrap' # Automatically adds echo "\n"; to every php -r call
# Checks for changes in local and remote dotfiles.
# Dotfiles repo is read from $BASHRC_DOTFILES_REPO_PATH; default path is ~/bashrc.
diff_dotfiles () {
for file in * ; do
diff " ${file} " ~ /" ${file} "
local localSubdir=" ${1:- .} "
local dotfilesRepo=" ${BASHRC_DOTFILES_REPO_PATH:- $(realpath ~/ bashrc)} "
pushd " ${dotfilesRepo} " > /dev/null
echo " Path: ${dotfilesRepo} "
for item in * ; do
local repoItem=" ${item} "
local localItem=" ~/${localSubdir} /${item} "
if [ -r " ${repoItem} " ] && [ -r " ${localItem} " ]; then
if [ -f " ${repoItem} " ] && [ -f " ${localItem} " ]; then
diff " ${repoItem} " " ${localItem} "
elif [ -d " ${repoItem} " ] && [ -d " ${localItem} " ]; then
local subdir=" ${item} "
local repoSubdir=" $( realpath ${repoItem} ) "
BASHRC_DOTFILES_REPO_PATH=" ${repoSubdir} " diff_dotfiles " ${subdir} "
fi
fi
done | less
popd > /dev/null
}
__wtfhd_sigint_trap_tmpl () {
echo ' __wtfhd_sigint_trap() {'
local oldTrap=" $1 "
echo " $oldTrap "
echo ' }'
}
# Shows files & dirs list sorted by size ascending.
# Search scope is function's argument or root directory if no argument supplied.
wtfhd () {
local oldSigintTrap=" $( trap -p SIGINT | awk ' {print $3}' | sed " s/^'//;s/'$//" ) "
eval " $( __wtfhd_sigint_trap_tmpl " ${oldSigintTrap} " ) "
trap __wtfhd_sigint_trap SIGINT
local path=" ${1:-/ } "
pushd " ${path} " > /dev/null
local sizeInfo=$( du -ahd 1 2> /dev/null | sort -h)
local sizeInfoRev=$( echo " ${sizeInfo} " | tac)
unset WTFHD_LARGEST_DIR
local largestDir=" $(
echo " ${sizeInfoRev} " | while read sizeInfoLine; do
local infoDir=$( echo " ${sizeInfoLine} " | awk ' {print $2}' )
if [ " ${infoDir} " != ' .' ] && [ -d " ${infoDir} " ] ; then
realpath " ${infoDir} "
break
fi
done
) "
if [ ! -z " ${largestDir} " ]; then
export WTFHD_LARGEST_DIR=" ${largestDir} "
fi
echo " ${sizeInfo} "
popd > /dev/null
trap " ${oldTrap:- -} " SIGINT
}
# Changes dir to largest found one.
# This function checks run results of last wtfhd() and changes directory to largest item if it is a directory, otherwise it does effectively nothing.
cdToLargest () {
if [ ! -z " ${WTFHD_LARGEST_DIR} " ] && [ -d " ${WTFHD_LARGEST_DIR} " ]; then
cd " ${WTFHD_LARGEST_DIR} "
fi
}
# Returns models of current Mac device, i.e. "Macbook5,1"
macdevModel () {
sysctlInfo=" $( sysctl hw.model 2> /dev/null) "
local sysctlResult=" $? "
if [ " ${sysctlResult} " -eq 0 ]; then
echo " ${sysctlInfo} " | sed -E ' s/^hw\.model\\s*[:=]//'
else
echo " Are you really using Mac?"
fi
unset sysctlInfo
return " ${sysctlResult} "
}
# Displays last used commands list.
# Usage: lastCmds [COUNT]
lastCmds () {
local DEFAULT_COUNT=10
local count=" $1 "
if ! [ " ${count} " -gt 0 ] 2> /dev/null; then
count=" ${DEFAULT_COUNT} "
fi
tail -n " ${count} " " ${HISTFILE} "
}
# Visualizes command return code
# Usage: __chkCmd [SHOW_OUTPUT] [COMMAND_1] [COMMAND_2] … [COMMAND_N]
# Commands stderr & stdout are enabled if and only if SHOW_OUTPUT is nonempty
__chkCmd () {
local suppressOutput
if [ " ${# 1} " -gt 0 ]; then
suppressOutput=" NO"
else
suppressOutput=" YES"
fi
shift
local CMD_LEN_LIMIT=40
local maxCmdLen=0
local cmds=()
local cmdOutputs=()
while [ " $# " -gt 0 ]; do
local cmd=" $1 "
shift
local displayCmd=" ${cmd:- <Nothing>} "
if [ " ${# cmd} " -gt " ${CMD_LEN_LIMIT} " ]; then
displayCmd=" ${displayCmd: 0: $((CMD_LEN_LIMIT - 1))} …"
fi
cmds+=( " ${displayCmd} " )
local displayCmdLen=" ${# displayCmd} "
if [ " ${displayCmdLen} " -gt " ${maxCmdLen} " ]; then
if [ " ${displayCmdLen} " -le " ${CMD_LEN_LIMIT} " ]; then
maxCmdLen=" ${displayCmdLen} "
else
maxCmdLen=" ${CMD_LEN_LIMIT} "
fi
fi
if [ " ${suppressOutput} " == " YES" ]; then
exec 3< & 0 4>&1 6>&2 < /dev/null > /dev/null 2> /dev/null
fi
local cmdResult
${cmd}
local cmdCode=" $? "
if [ " ${cmdCode} " -eq 0 ]; then
cmdResult=" True/Success "
else
cmdResult=" False/Failure"
fi
cmdOutputs+=( " ${cmdResult} (RET: ${cmdCode} )" )
if [ " ${suppressOutput} " == " YES" ]; then
exec 0< & 3 3< & - 1>&4 4>& - 2>&6 6>& -
fi
done
for cmdN in $( seq 0 $(( ${# cmds[*]} - 1 )) ) ; do
printf " %${maxCmdLen} s: %s\n" " ${cmds[${cmdN}]} " " ${cmdOutputs[${cmdN}]} "
done
}
# Visualizes command return code
# Usage: chkCmd [COMMAND_1] ';' [COMMAND_2] ';' … ';' [COMMAND_N]
# NOTE: stderr & stdout are redirected to /dev/null while executing commands
chkCmd () {
__chkCmd ' ' " $@ "
}
# Visualizes command return code
# Usage: chkCmdDbg [COMMAND_1] ';' [COMMAND_2] ';' … ';' [COMMAND_N]
# NOTE: commands output is fully preserved
chkCmdDbg () {
__chkCmd ' SHOW' " $@ "
}
# Shortcut for 'open -a "Keychain Access"'
openKeychain () {
open -a ' Keychain Access'
}
gobjc () {
gcc -framework Foundation -include ' Foundation/Foundation.h ' " $@ "
localCC () {
localCompile " $1 " ' gcc '
}
clobjc () {
clang -framework Foundation -include ' Foundation/Foundation.h ' " $@ "
localObjC () {
localCompile " $1 " ' clobjc '
}
jsonpp () {
python -c ' import sys, json; print json.dumps (json.loads (sys.stdin.read ()), ensure_ascii = False, indent = 2, separators = (",", ": "))'
localCompile () {
local src=" $1 "
shift
local compiler=" $@ "
local found=" "
local foundTwice=" "
local ext=" "
local sourceFile=" "
for maybeExt in " " " .c" " .m" " .cpp" " .cxx" ; do
local maybeSourceFile=~ /local/src/" ${src}${maybeExt} "
if [ -f " ${maybeSourceFile} " ]; then
if [ ! -z " ${found} " ]; then
echo ' Ambigous source file' " ${src} " >&2
return 1
else
found=" YES"
ext=" ${maybeExt} "
sourceFile=" ${maybeSourceFile} "
fi
fi
done
if [ -z " ${found} " ]; then
echo ' Source file' " ${src} " ' is not found'
return 2
fi
local executable=~ /local/bin/" $( basename " ${src} " " ${ext} " ) "
${compiler} " ${sourceFile} " -o " ${executable} "
}
clear_tmp () {
local TMP_DIR=~ /" tmp"
local MAX_AGE_DAYS=7 # Delete files older than a week
local MIN_CLEAN_INTERVAL=$(( 3600 )) # At least one hour between cleans
local LAST_CLEAN_FILE=" ${TMP_DIR} /.lastclean"
local currentTimestamp=" $( date ' +%s' ) "
local lastCleanDate=" $( stat " ${LAST_CLEAN_FILE} " -c ' %Y' ) "
if [ $(( currentTimestamp - lastCleanDate )) -lt " ${MIN_CLEAN_INTERVAL} " ]; then
return 0
fi
touch " ${LAST_CLEAN_FILE} "
find " ${TMP_DIR} " -mindepth 1 -mtime " +${MAX_AGE_DAYS} " -not -wholename " ${LAST_CLEAN_FILE} " -delete
}
clear_tmp
# Moves a git tag
git_move_tag () {
local tag=" $1 "
shift
local tagMessage=" $( git cat-file -p $( git rev-parse " ${tag} " ) | tail -n +6) "
git tag -d " ${tag} " && git push origin " :refs/tags/${tag} " && git tag -a " ${tag} " -m " ${tagMessage} " " $@ " && git push --tags
}
# Converts a video to GIF
vid2gif () {
local video=" $1 "
local gif=" $2 "
}
# Lists all functions defined in this file
@@ -403,7 +807,7 @@ func_help() {
local info_code=$( declare -F " ${func} " | awk ' {print "local first_line=\""$2"\""; printf "local source_file=\""; for (i = 3; i < NF; i++) {printf $i" ";} printf $NF"\"";}' )
eval " ${info_code} "
local help_lines=0
while head -n $(( first_line - help_lines - 1 )) " ${source_file} " | tail -n 1 | egrep -o ' ^\s*#' > /dev/null 2 > /dev/null; do
while head -n $(( first_line - help_lines - 1 )) " ${source_file} " | tail -n 1 | egrep -o ' ^\s*#' & > /dev/null; do
help_lines=$(( help_lines + 1 ))
done
if [ $(( help_lines)) -gt 0 ]; then
@@ -415,3 +819,9 @@ func_help() {
fi
fi
}
# The next line updates PATH for the Google Cloud SDK.
source ' /Users/byss/google-cloud-sdk/path.bash.inc'
# The next line enables shell command completion for gcloud.
source ' /Users/byss/google-cloud-sdk/completion.bash.inc'