I wrote this script template at work to save typing repetitive commands and to improve my Bash-fu. I'd like to know of any moderate-to-severe quoting, robustness, security or usability issue. All code provided in answers should work in Bash 4.
.sedevrc:
#!/bin/bash
se_projects=()
se_cvs_dir="${USER}@172.0.0.1/export/home/cvsrootdir"
#============PROJECT CONFIG START================#
declare -A product
product[CVSROOT]="${se_cvs_dir}/product"
product[SOURCEDIR]="${HOME}/product"
product[WORKDIR]="${HOME}/test"
product[MAINPRG]="${product[SOURCEDIR]}/build/product"
se_projects+=("${!product@}")
#================================================#
# Other products in the same format as above
#=============PROJECT CONFIG END=================#
# se user commands
function secvs
{
se_loop_projects "_se_cvs" "$@"
}
function sectags
{
se_loop_projects "_se_ctags" "$@"
}
function semake
{
se_loop_projects "_se_make" "$@"
}
function sealias
{
se_loop_projects "_se_alias" "$@"
}
# se helper functions
function _se_cvs
{
echo_info "Updating ${project}..."
local cvsroot=${project}[CVSROOT]
CVSROOT=:pserver:${!cvsroot:?}
local sourcedir=${project}[SOURCEDIR]
cd ${!sourcedir:?}
cvs -q update -Pd && echo_noerror "Update ${project} complete" || echo_error "Update ${project} failed"
}
function _se_ctags
{
echo_info "Tagging ${project}..."
local sourcedir=${project}[SOURCEDIR]
cd ${!sourcedir:?}/src
{ file tags | grep 'Ctags tag' >/dev/null; } || rm -f tags
ctags -R --exclude=obj && echo_noerror "Tag ${project} complete" || echo_error "Tag ${project} failed"
}
function _se_make
{
echo_info "Building ${project}..."
local sourcedir=${project}[SOURCEDIR]
cd ${!sourcedir:?}
if [[ -d build ]] && (( $(bc <<< "$(date +%s) - $(date -r build +%s)") > 60 * 60 * 24 * 5 ))
then
make clean || echo_error "Clean ${project} failed"
fi
make && echo_noerror "Build ${project} complete" || echo_error "Build ${project} failed"
}
function _se_alias
{
local workdir=${project}[WORKDIR]
local mainprg=${project}[MAINPRG]
alias ${project}="cd ${!workdir:?} && ${!mainprg:?}"
}
# Run an se helper function over selected projects
function se_loop_projects
{
local helper_function_name=$(declare -F $1)
if [[ ! ${helper_function_name} =~ ^_se_* ]]
then
echo_error "'$1' is not in the list of available se helper functions: $(declare -F | cut -d' ' -f3 | grep '^_se_*' | xargs)"
if [[ -z $1 ]]
then
echo_error "Usage: ${FUNCNAME[0]} FUNCTION [PROJECTS]"
fi
return 1
fi
shift
local -i project_count=$(( $# > 0 && $# < ${#se_projects[@]} ? $# : ${#se_projects[@]} ))
for (( i=0; i<${project_count}; i++ ))
do
local project=${1:-${se_projects[$i]:?}}
shift
if [[ ${se_projects[@]} =~ ${project} ]]
then
local helper_function=helper_function_name
if [[ ${helper_function_name} == '_se_alias' ]] # FIXME: hardcoding
then
${!helper_function} # Safer than `eval '"${helper_function}"'`
else
set -e
(${!helper_function}) &
set +e
fi
else
local IFS=','
echo_warning "'${project}' is not in the list of available se projects: ${se_projects[*]}"
fi
done
wait
while (( $# > 0 ))
do
echo_warning "Too many arguments, omitting '$1'"
shift
done
}
# Customized echoes
echo_reset_color=$(tput sgr0)
function echo_info
{
local echo_info_color=$(tput setaf 4) # Blue
echo "${echo_info_color}$@${echo_reset_color}"
}
function echo_noerror
{
local echo_noerror_color=$(tput setaf 2) # Green
echo "${echo_noerror_color}$@${echo_reset_color}"
}
function echo_warning
{
local echo_warning_color=$(tput setaf 3) # Orange
>&2 echo "${echo_warning_color}$@${echo_reset_color}"
}
function echo_error
{
local echo_error_color=$(tput setaf 1) # Red
>&2 echo "${echo_error_color}$@${echo_reset_color}"
}
# Autocomplete project names for se user commands
complete -W "${se_projects[*]}" $(declare -F | cut -d' ' -f3 | grep '^se[a-z]\+' | xargs)
To run it interactively,
[gao@hostname ~]-bash4.1.2$ secvs product1
[1] 16516
Updating product1...
? src/hello.cc
M Makefile
Update product1 complete
[1]+ Done ( ${!helper_function} )
[gao@hostname ~]-bash4.1.2$ secvs hats product1 stackoverflow
'hats' is not in the list of available se projects: product1,product2
[1] 16590
Updating product1...
M Makefile
Update product1 complete
[1]+ Done ( ${!helper_function} )
Too many arguments, omitting 'stackoverflow'
[gao@hostname ~]-bash4.1.2$ sectags
[1] 16519
Tagging product2...
[2] 16520
Tagging product1...
Tag product1 complete
Tag product2 complete
[1]- Done ( ${!helper_function} )
[2]+ Done ( ${!helper_function} )
To run it via crontab,
[gao@hostname ~]-bash4.1.2$ cat ~/cronjob
#!/bin/bash
# Nullify commands that would trigger unwanted mail notification when run by cron
function echo { return; }
function tput { return; }
# Run se user commands
source "${HOME}/.sedevrc"
secvs | grep '^C ' # Show only conflicts
sectags
semake >/dev/null
[gao@hostname ~]-bash4.1.2$ crontab -l
7 7 * * 1-5 ${HOME}/cronjob