# POSIX shell environment # # This script contains functions, aliases and settings that are common for all # POSIX-compatible shells including bash, ksh, and zsh. # # $Id: shrc 1618 2008-03-06 23:24:24Z mikel $ # ensure this shell supports local variables using either local # (from bash and zsh) or typeset (from ksh) if ! type local >/dev/null 2>&1 then if type typeset >/dev/null 2>&1 then # must be a function not an alias so it works in ksh too local() { typeset "$@" } fi # see if our attempts to emulate "local" worked if ! type local >/dev/null 2>&1 then echo "Cannot find local or typeset shell command, some features in .shrc will not work" 1>&2 fi fi ### HELPER FUNCTIONS # # add a directory to the path addpath() { local _adddir local _position local _pathvar local _separator _adddir=$1 _position=$2 _pathvar=${3:-"PATH"} _separator=${4:-":"} OIFS="$IFS" IFS="$_separator" case "$_position" in start) delpath "$_adddir" "$_pathvar" "$_separator" eval $_pathvar="\$_adddir""\${$_pathvar:+\$_separator}""\$$_pathvar" ;; end) delpath "$_adddir" "$_pathvar" "$_separator" eval $_pathvar="\$$_pathvar""\${$_pathvar:+\$_separator}""\$_adddir" ;; *) local dirs local seen eval dirs=\$$_pathvar for d in $dirs do # directory is already in PATH, leave as is test "${_adddir:-.}" = "${d:-.}" && seen=true done test -z "$seen" && eval $_pathvar="\$$_pathvar""\${$_pathvar:+$_separator}""\$_adddir" ;; esac IFS="$OIFS" } # set up a new directory (and in future, a whole clean environment) # for answering a question on the StackExchange network # the directory name is based on the supplied question URL, # e.g. http://unix.stackexchange.com/questions/11815/what-should-i-use-when-cut-doesnt-cut-it # so that I can go back there easily in future answer() { local url=$1 if test -z "$url" then echo "med: need a URL" 1>&2 return 0 fi read -r site questionno </dev/null) EOF } statusmessage() { local _status="$1" case $_status in 0) echo "done" ;; 20) # zsh uses status 20 for suspended, don't print anything ;; *) echo "error $_status" ;; esac } # remove a line from a file delline() { local _line local _file _line=$1 _file=$2 echo "delline $*" if test -z "$_line" -o -z "$_file" then error "Usage: delline " return 1 fi sed -i.bak -e "${_line}d" "$_file" } # remove a directory from the path delpath() { local _deldir local _pathvar local _separator _deldir=$1 _pathvar=${2:-"PATH"} _separator=${3:-":"} NIFS="$IFS" IFS="$_separator" local _newdirs local _olddirs eval _olddirs=\$$_pathvar _newdirs= for d in $_olddirs do test "${d:-.}" = "${_deldir:-.}" && continue _newdirs="$_newdirs""${_newdirs:+$_separator}""$d" done eval $_pathvar="\$_newdirs" IFS="$NIFS" } # print an error message error() { echo "$@" 1>&2 } # return true if the command can be found, false otherwise exists() { local cmd cmd="$1" if type "$cmd" >/dev/null 2>&1 then return 0 else return 1 fi } # prevent running "exit" if the user is still running jobs in the background # the user is expected to close the jobs or disown them _exit() { case $- in *m*) # this way works in bash and zsh jobs | wc -l | grep -q '^ *0 *$' if test $? -eq 0 then command exit "$@" else jobs fi ;; *) command exit "$@" ;; esac } # print lines from a file # starting with the line matching $1 # finishing with the line matching $2 from_to() { local from local to from=$1 to=$2 shift 2 sed -ne "/$from/ b INSIDE d : INSIDE { p /$to/ q n b INSIDE } : END" "$@" } # return the last component of the path # (like basename, but guaranteed to be available) last_part_of_path() { local full_path="$1" full_path="${full_path%/}" if test -n "$full_path" then echo "${full_path##*/}" else echo "/" fi } mtd() { local dir dir=$(mktemp -d) if test -n "$dir" then if test -d "$dir" then echo "$dir" cd "$dir" else echo "mktemp directory $dir does not exist" fi else echo "mktemp didn't work" fi } # display a desktop notification # (requires support in the terminal application) notify() { local message="$*" # set the title and then ring the bell # in my patched version of GNOME Terminal, this displays a desktop notification case $TERM in putty|xterm*) settitle "$message" bell ;; esac } on_cygwin() { case `uname -s` in CYGWIN*) true ;; *) false ;; esac } plist() { ps -fp $(pgrep -d , "$@") } # process list with status pss() { ps -e -o pid,stat,stime,args } # run a simple command quietly quiet() { "$@" >/dev/null 2>&1 return $? } # set the xterm title settitle() { test -z "${titlestart}" && return 0 printf "%b%s%b" "${titlestart}" "$*" "${titlefinish}" } terminit() { # Cygwin should set the terminal type to cygwin, but doesn't if on_cygwin then if test "$TERM" = "dumb" then TERM=cygwin fi fi # determine the graphics mode escape sequences if exists tput then if quiet tput longname then bold="$(tput bold)" underline="$(tput smul)" standout="$(tput smso)" normal="$(tput sgr0)" black="$(tput setaf 0)" red="$(tput setaf 1)" green="$(tput setaf 2)" yellow="$(tput setaf 3)" blue="$(tput setaf 4)" magenta="$(tput setaf 5)" cyan="$(tput setaf 6)" white="$(tput setaf 7)" khome="$(tput khome)" kend="$(tput kend)" kdch1="$(tput kdch1)" fi fi # determine the window title escape sequences case "$TERM" in aixterm|dtterm|putty|rxvt|xterm*) titlestart=']0;' titlefinish='' #titlestart='\033]0;' #titlefinish='\007' ;; cygwin) titlestart='];' titlefinish='' ;; konsole) titlestart=']30;' titlefinish='' ;; screen*) # status line #titlestart='_' #titlefinish='\' # window title titlestart='k' titlefinish='\' ;; *) if exists tput then if quiet tput longname then titlestart="$(tput tsl)" titlefinish="$(tput fsl)" fi else titlestart='' titlefinish='' fi ;; esac } sub() { echo "$3" | sed -e "s/$1/$2/" } # get the username of the person we originally logged in as # (e.g. print mward even if we did sudo -s, $LOGNAME does this wrong) username() { #logname # doesn't work with screen, urxvt, etc. echo "$LOGNAME" } ### SHELL ENVIRONMENT # set shell options and environment HISTIGNORE="bg:fg: *" HISTSIZE=128 if test -z "$HOST" then HOST=$(hostname) fi if exists tty then TTY=$(tty) else TTY='?' fi if test -z "$UID" then UID=$(id -u) fi USER=$(whoami) # set directories to search for commands # add preferred locations at start # X Window system directories are deprecated on most Linux distributions # where they are now just symlinks to /usr/bin # if we add the symlink versions before the standard directories such as # /usr/bin, some utilities get confused, so only add them to PATH if # they're not symlinks for dir in /usr/X11R6/bin /usr/bin/X11 /usr/java1.6/bin do test -d "$dir" && test ! -L "$dir" && addpath "$dir" start done get_android_sdk_dir() { _platform=$(uname -m) case $_platform in i686) _platform=x86 ;; esac _os=$(uname -s | tr '[[:upper:]]' '[[:lower:]]') _sdkdir=$HOME/android-sdk-${_os}_${_platform} echo $_sdkdir } android_sdk_dir=$(get_android_sdk_dir) for dir in /usr/kerberos/bin /usr/posix/bin /usr/gnu/bin /usr/local/bin "$android_sdk_dir/tools" "$HOME/depot_tools" "$HOME"/.cabal/bin "$HOME"/bin do test -d "$dir" && addpath "$dir" start done # add standard locations if they're not there for dir in /usr/bin /bin /usr/sbin /sbin do test -d "$dir" && addpath "$dir" done # add the current directory to the end for non-root users if test $UID -ne 0 then for dir in . do test -d "$dir" && addpath "$dir" end done fi export PATH # set directories to search for documentation for dir in /usr/local/share/info /usr/local/info "$HOME"/info do test -d "$dir" && addpath "$dir" start INFOPATH done for dir in /usr/share/info /usr/info do test -d "$dir" && addpath "$dir" end INFOPATH done export INFOPATH for dir in /usr/X11R6/man /usr/kerberos/man /usr/posix/man /usr/gnu/man /usr/local/share/man /usr/local/man "$HOME"/man do test -d "$dir" && addpath "$dir" start MANPATH done for dir in /usr/share/man /usr/man do test -d "$dir" && addpath "$dir" end MANPATH done export MANPATH ### ALIASES AND FUNCTIONS # create aliases for commonly invoked commands if quiet alias then # aliase apt to aptitude or apt-get (in that order) alias add='git add' alias apply='git apply' type apt-get >/dev/null 2>/dev/null && alias apt='apt-get' type aptitude >/dev/null 2>/dev/null && alias apt='aptitude' alias b='bg' alias branch='git branch' alias c='scp' case $(uname -s) in CYGWIN*) for drive in c d e f g h i j k l m n o p q r s t u v w x y z do alias $drive:="cd $(cygpath -u "$drive:")" done ;; esac alias clone='git clone' alias checkout='git checkout' alias cx='chmod +x' alias d='dirs' # only supported by bash and zsh alias di='git diff' alias e='${EDITOR:-vi}' alias edit='${EDITOR:-vi}' alias f='fg' alias fetch='git fetch' alias g='grep -EHIin' # -H and -I are non-POSIX but are widely supported alias gdb='gdb -q ' alias gitd='git diff' alias gitdc='git diff --cached' alias gits='git status' #alias h=head alias html='cd /var/www/html' alias hup='kill -HUP' alias l='ls' alias l.='l -d .*' alias l1='l -1' alias la='l -A' alias lc='l -C' alias ll='l -l' alias llt='l -lt' alias lltr='l -ltr' alias lt='l -t' alias ltr='l -tr' alias log='git log' alias logg='git logg' alias j='jobs' # replaced by shell function #alias m=make alias merge='git merge' for section in 1 2 3 4 5 6 7 8 9; do alias man$section="man $section" done alias nohup='nohup ' # expand aliases after nohup alias p='${PAGER:-less}' alias pd='pushd' alias po='popd' alias psme='ps -f -U "${USER:-${LOGNAME}}"' alias pull='git pull' alias push='git push' alias py='python' alias py3='python3' alias q='_exit' alias rb='reportblocked' alias rcslog='rlog' alias rdesktop='rdesktop -M' alias rdiff='rcsdiff' alias remote='git remote' alias rg='reportgraylist' alias root='root ' # expand aliases alias ssh='ssh -A' alias s='ssh' alias show='git show' alias ss='sudo -s' # XXX clashes with upstart's "status" command alias status='git status' alias strace='strace ' # expand aliases after strace alias sudo='sudo ' # expand aliases after sudo #alias t=tail alias tag='git tag' alias tf='tail -n 0 -F' alias tm='t -f /var/log/mail' alias today='date "+%Y%m%d"' alias tp='t -f "$HOME/.proclog"' alias track='git add --intent-to-add' alias untrack='git reset' alias v='${VIEWER:-view}' alias x='_exit' # set default flags if quiet grep --color=auto --quiet "" "$HOME"/.shrc then alias grep='grep --color=auto' fi if quiet ls --color=never --directory / then # enable colors with GNU ls # LANG=C gives traditional sort order and date format alias ls='LANG=C ls -1 --color=auto' # disable colors, mark files with a special trailing character #alias ls='ls --color=never --classify --format=across' elif test "$(CLICOLOR_FORCE=1 /bin/ls -1G 2>/dev/null)" != "$(/bin/ls -1G 2>/dev/null)" then # enable colors with FreeBSD ls alias ls='ls -1G' elif quiet /bin/ls -1F then # fall back to appending a file type "flag" alias ls='ls -1F' fi fi # ring the terminal's bell bell() { printf "\a" } # perform a command on all lines except the first, # e.g. ps | body grep ps body() { IFS= read -r header printf '%s\n' "$header" "$@" } # do some floating point arithmetic calc() { echo "scale=3; $*" | bc } directories() { if exists dirs then dirs -v else echo "$PWD" fi } # edit the most recently changed file editlatest() { edit $(latest "$@") } # print the first screenful of text from a file h() { local lines local promptlines promptlines=5 # leave five lines for the prompt if test -n $LINES then lines=$(($LINES - $promptlines)) else lines=$(tput lines) if test -z $lines then lines=24 fi fi head -n $lines "$@" } # show the most recently changed file latest() { local count=1 # how many files to print local promptlines=5 # how many lines to leave for the prompt # when printing a screenful with -s local usage="Usage: latest [-n ] [-s] [--] [pattern]" while test $# -gt 0 do case $1 in # -- = stop processing options --) shift break ;; # -n = files -n) if test -n "$2" then count=$2 shift 2 else echo "$usage" 1>&2 return 1 fi ;; # -s = one screenful -s) count=$((LINES - promptlines)) shift ;; # - = files -[0-9]*) count=${1#-} shift ;; # anything else starting with a minus as a usage error -*) echo "$usage" 1>&2 return 1 ;; *) break ;; esac done if test $# -ne 0 then /bin/ls -t -1 -d "$@" | head -n $count else /bin/ls -t -1 -d * | head -n $count fi } # make with an automatic log and pager m() { command make "$@" 2>&1 1>&- 1>make.log | tee -a make.log } # make a directory and cd to it mcd() { if test -d "$1"; then echo "$1 already exists" else mkdir -p "$1" && cd "$1" fi } # flash the screen ("visible bell") flash() { if exists tput then tput flash fi } # sort a file in place # Usage: isort ... isort() { for file do temp=$file.$$ if test -f "$temp" then error "$temp already exists, skipping $file" else if sort "$file" > "$temp" then if mv "$temp" "$file" then # OK : else error "isort: Cannot move temp file $temp for $file" rm "$temp" fi else error "isort: Error running sort on $file" rm "$temp" fi fi done } programs() { local programfiles="$(cygpath -u "$PROGRAMFILES")" if test -n "$programfiles" && test -d "$programfiles" then cd "$programfiles/$1" else return 1 fi } # show the 10 most recently changed files recent() { if test $# -ne 0 then /bin/ls -t -1 "$@" | head -n 10 else /bin/ls -t -1 . | head -n 10 fi } # keep trying a command until it works # (e.g. ping -c 1 host) retry() { while true do "$@" if test $? -eq 0 then bell break else sleep 10 fi done } # search for a pattern in text files in a directory tree rgrep() { local pattern local dirs local args # XXX this doesn't look right # I think we need to add explicit support for each grep option # (with the first parameter being the pattern and the rest being a list of files or directories, # as it currently is) args= #while getopts "A:aB:C:bcD:d:Ee:FPf:GHhIiLlm:noqRsUuVvwxyZ" flag... while true do case $1 in -*) args="${args:+$args }$1" shift ;; *) break ;; esac done if test $# -ge 1 then pattern="$1" shift fi find "${@:-.}" \( -type d \( -name ".svn" -o -name ".git" \) -prune \) -o -type f -print0 | xargs -0i grep $args "$pattern" {} /dev/null } # print the escape sequence for the named color (e.g. setcolor "blue") setcolor() { local color color="$1" if test -n "$color" then eval echo -n "\"\$$color\"" fi } # get a short version of the hostname for use in the prompt or window title short_hostname() { local hostname local hostprefix hostname="$HOST" if test -n "$hostname" then # strip domain name and any hyphen prefix (assumed to be a username) # XXX what if it's some other username? hostname=$(echo "$hostname" | sed -e "s/\..*//" -e "s/^[^-]*-//") echo "$hostname" return 0 else echo "unknown" return 1 fi } # look up the password for a user@host combination pass() { typeset user typeset host if test -z "$PASSFILE" then error "PASSFILE not set" return 1 fi if expr "$1" : "..*@..*" >/dev/null then user=${1%%@*} host=${1##*@} else host="$1" user="$2" fi if test -n "$user" then awk "\$1 == \"$host\" && \$2 == \"$user\" { printf \"%s\n\", \$3 }" "$PASSFILE" else awk "\$1 == \"$host\" { printf \"%s\t%s\n\", \$2, \$3 }" "$PASSFILE" fi } vipass() { "$EDITOR" "$PASSFILE" } prompt_character() { case ${laststatus:-0} in 0|20|148) # 0 is OK, 20 is command suspended in zsh, 148 is bash case $UID in "") echo "?" ;; 0) echo "#" ;; *) #echo ">" echo "$" ;; esac ;; *) echo "?" ;; esac } # print the last screenful of text from a file t() { local lines local promptlines promptlines=5 # leave five lines for the prompt if test -n $LINES then lines=$(($LINES - $promptlines)) else lines=$(tput lines) if test -z $lines then lines=24 fi fi tail -n $lines "$@" } # print the current directory with $HOME changed to ~ tilde_directory() { printf "%b%s%b" "$blue" "$(pwd | sed -e 's#'"$HOME"'#~#')" "$normal" } # list all instances of a command where() { type -a "$@" } # show which YUM repository/repositories have the named package # (a bit like apt-cache policy) yumpolicy() { repoquery -a --qf '%-20{repoid} %{name}-%{version}' "$@" } zones() { if test -d /var/named/chroot/var/named/master then cd /var/named/chroot/var/named/master else cd /var/named/master fi } ### PROGRAM SETTINGS # set preferred generic utilities if test -n "$DISPLAY" then exists netscape && export BROWSER=netscape exists mozilla && export BROWSER=mozilla exists opera && export BROWSER=opera exists firefox && export BROWSER=firefox exists chrome && export BROWSER=chrome exists google-chrome && export BROWSER=google-chrome exists chromium && export BROWSER=chromium exists chromium-browser && export BROWSER=chromium-browser else exists lynx && export BROWSER=lynx exists links && export BROWSER=links exists elinks && export BROWSER=elinks fi exists rsh && export CVS_RSH=rsh exists ssh && export CVS_RSH=ssh exists diff && export DIFF=diff exists ed && export EDITOR=ed exists vi && export EDITOR=vi exists vim && export EDITOR=vim exists more && export VIEWER=more exists less && export VIEWER=less exists view && export VIEWER=view exists more && export PAGER=more exists less && export PAGER=less export DIFF # for use by ~/bin/svndiff export VISUAL="$EDITOR" export WINTERM=xterm if test -f "$HOME"/.keychain/"$HOST"-sh then . "$HOME"/.keychain/"$HOST"-sh fi # set file locations test -r "$HOME"/.inputrc && export INPUTRC="$HOME"/.inputrc # set preferred program options export CLICOLOR=true export FIGNORE= # vt220/xterm: 1=bold, 4=underline, 7=standout/reverse export GREP_COLOR=4 # disable new GNU grep colors for file names and line numbers export GREP_COLORS=fn=:ln=:bn=:se= export LESS=-eFj3MRX test -n "$TABSIZE" && export LESS="${LESS}x${TABSIZE}" export TOP=-I export WANT_SSH_AGENT=true export WWW_HOME=http://google.com export PYTHONSTARTUP="$HOME"/.pythonstartup.py test -f "$HOME"/.env.local && . "$HOME"/.env.local taskbar() { local njobs=${#jobtexts[*]} local ncolumns=${COLUMNS:-80} local maxjobwidth=18 if test ${njobs:=0} -gt 0; then local jobwidth=$((ncolumns/njobs - 2)) if test $jobwidth -gt $maxjobwidth; then jobwidth=$maxjobwidth fi for jobnum in ${(k)jobtexts[*]}; do local jobtext="${jobtexts[$jobnum]}" printf "[%-${jobwidth}s]" "${jobtext[0,$jobwidth]}" done printf "\n" fi } true # vi: set ts=4 sw=4 noet: