# composure - by erichs # light-hearted shell functions for intuitive shell programming # install: source this script in your ~/.profile or ~/.${SHELL}rc script # latest source available at http://git.io/composure # known to work on bash, zsh, and ksh93 # define default metadata keywords: about () { :; } group () { :; } param () { :; } author () { :; } example () { :; } cite () { about creates a new meta keyword for use in your functions param 1: keyword example $ cite url example $ url http://somewhere.com group composure # this is the storage half of the 'metadata' system: # we create dynamic metadata keywords with function wrappers around # the NOP command, ':' # anything following a keyword will get parsed as a positional # parameter, but stay resident in the ENV. As opposed to shell # comments, '#', which do not get parsed, thus are not available # at runtime. # a BIG caveat--your metadata must be roughly parsable: do not use # contractions, and consider single or double quoting if it contains # non-alphanumeric characters typeset keyword for keyword in $*; do eval "function $keyword { :; }" done } draft () { about wraps last command into a new function param 1: name to give function example $ ls example $ draft list example $ list group composure typeset func=$1 eval 'function ' $func ' { ' $(fc -ln -1) '; }' typeset file=$(mktemp /tmp/draft.XXXX) typeset -f $func > $file transcribe $func $file draft rm $file 2>/dev/null } glossary () { about displays help summary for all functions, or summary for a group of functions param 1: optional, group name example $ glossary example $ glossary misc group composure typeset targetgroup=${1:-} for func in $(listfunctions); do typeset about="$(metafor $func about)" if [ -n "$targetgroup" ]; then typeset group="$(metafor $func group)" if [ "$group" != "$targetgroup" ]; then continue # skip non-matching groups, if specified fi fi letterpress "$about" $func done } letterpress () { typeset metadata=$1 leftcol=${2:- } rightcol if [ -z "$metadata" ]; then return fi OLD=$IFS; IFS=$'\n' for rightcol in $metadata; do printf "%-20s%s\n" $leftcol $rightcol done IFS=$OLD } listfunctions () { # unfortunately, there does not seem to be a easy, portable way to list just the # names of the defined shell functions... # here's a hack I modified from a StackOverflow post: # we loop over the ps listing for the current process ($$), and print the last column (CMD) # stripping any leading hyphens bash sometimes throws in there typeset x ans typeset this=$(for x in $(ps -p $$); do ans=$x; done; printf "%s\n" $ans | sed 's/^-*//') case "$this" in bash) typeset -F | awk '{print $3}' ;; *) # trim everything following '()' in ksh typeset +f | sed 's/().*$//' ;; esac } metafor () { about prints function metadata associated with keyword param 1: function name param 2: meta keyword example $ metafor glossary example group composure typeset func=$1 keyword=$2 # this sed-fu is the retrieval half of the 'metadata' system: # first 'cat' the function definition, # then 'grep' for the metadata keyword, and # then parse and massage the matching line typeset -f $func | sed -n "s/;$//;s/^[ ]*$keyword \([^([].*\)*$/\1/p" } reference () { about displays apidoc help for a specific function param 1: function name example $ reference revise group composure typeset func=$1 typeset about="$(metafor $func about)" letterpress "$about" $func typeset params="$(metafor $func param)" if [ -n "$params" ]; then printf "parameters:\n" letterpress "$params" fi typeset examples="$(metafor $func example)" if [ -n "$examples" ]; then printf "examples:\n" letterpress "$examples" fi } revise () { about loads function into editor for revision param 1: name of function example $ revise myfunction group composure typeset func=$1 typeset temp=$(mktemp /tmp/revise.XXXX) # populate tempfile... if [ -f ~/.composure/$func.inc ]; then # ...with contents of latest git revision... cat ~/.composure/$func.inc >> $temp else # ...or from ENV if not previously versioned typeset -f $func >> $temp fi if [ -z "$EDITOR" ] then typeset EDITOR=vi fi $EDITOR $temp source $temp transcribe $func $temp revise rm $temp } transcribe () { typeset func=$1 typeset file=$2 typeset operation="$3" if git --version >/dev/null 2>&1; then if [ -d ~/.composure ]; then ( cd ~/.composure if git rev-parse 2>/dev/null; then if [ ! -f $file ]; then printf "%s\n" "Oops! Couldn't find $file to version it for you..." return fi cp $file ~/.composure/$func.inc git add --all . git commit -m "$operation $func" fi ) else if [ "$USE_COMPOSURE_REPO" = "0" ]; then return # if you say so... fi printf "%s\n" "I see you don't have a ~/.composure repo..." typeset input typeset valid=0 while [ $valid != 1 ]; do printf "\n%s" 'would you like to create one? y/n: ' read input case $input in y|yes|Y|Yes|YES) ( echo 'creating git repository for your functions...' mkdir ~/.composure cd ~/.composure git init echo "composure stores your function definitions here" > README.txt git add README.txt git commit -m 'initial commit' ) # if at first you don't succeed... transcribe "$func" "$file" "$operation" valid=1 ;; n|no|N|No|NO) printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message." valid=1 ;; *) printf "%s\n" "sorry, didn't get that..." ;; esac done fi fi } : <