core.sh
Go to the documentation of this file.
00001 ############################################################################## 00002 # @file core.sh 00003 # @brief Core functions for Bash development. 00004 # 00005 # This is the core module of the BASIS utilities for Bash. It implements 00006 # fundamental functions for the development in Bash. Therefore, this module 00007 # has to be kept independent of any other modules and shall only make use 00008 # of Bash builtin's and basic commands. 00009 # 00010 # Copyright (c) 2011, 2012 University of Pennsylvania. All rights reserved.<br /> 00011 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file. 00012 # 00013 # Contact: SBIA Group <sbia-software at uphs.upenn.edu> 00014 ############################################################################## 00015 00016 [ "${_BASIS_CORE_INCLUDED}" == 'true' ] || { 00017 _BASIS_CORE_INCLUDED='true' 00018 00019 00020 . "`cd -P -- \`dirname -- "${BASH_SOURCE}"\` && pwd`/config.sh" || exit 1 00021 00022 00023 ## @addtogroup BasisBashUtilities 00024 # @{ 00025 00026 00027 # ============================================================================ 00028 # module import 00029 # ============================================================================ 00030 00031 # ---------------------------------------------------------------------------- 00032 ## @brief Import Bash module. 00033 # 00034 # This function can be used to import, i.e., source, a named module, where the 00035 # module is searched for in the directories specified by the @c BASHPATH 00036 # (environment) variable. Modules can be prepended by the names of the 00037 # subdirectories they are located in relative to a directory listed in the 00038 # @c BASHPATH, where dots (.) can be used instead of slashes to separate the 00039 # parts of the relative module path. Moreover, the <tt>.sh</tt> extension of 00040 # the modules is appended to the module name if not specified. 00041 # 00042 # Example: 00043 # @code 00044 # import sbia.basis.core 00045 # import -o optional.module 00046 # if [ $? -eq 0 ]; then 00047 # echo "Module optional.module imported successfully" 00048 # else 00049 # echo "Module optional.module not found" 00050 # fi 00051 # @endcode 00052 # 00053 # @note The directories listed in the @c BASHPATH must be absolute paths 00054 # starting with a forward slash (/). 00055 # 00056 # @param [in] options Option flags. The only possible option is -o. If this 00057 # option is given before the module name, the function 00058 # returns with return value 1 on error. Otherwise, it 00059 # calls the exit function with this exit code. 00060 # It can therefore be used for optional modules, which 00061 # do not necessarily need to be imported. 00062 # @param [in] module Name of the module to import. 00063 # 00064 # @returns Only on success or if the -o option is given. Otherwise it calls 00065 # the exit function if an error occurs. 00066 # 00067 # @retval 0 if the module was loaded successfully. 00068 # @retval 1 on error if -o option is given. 00069 import() 00070 { 00071 if [[ -z "${BASHPATH}" ]]; then 00072 echo "import: BASHPATH not specified!" 1>&2 00073 exit 1 00074 fi 00075 local exitonerror='true' 00076 while [ $# -gt 0 ]; do 00077 case "$1" in 00078 -o) exitonerror='false'; ;; 00079 *) break; ;; 00080 esac 00081 shift 00082 done 00083 local module="${1//.//}" # replace dots (.) by forward slashes (/) 00084 if [[ -z "${module}" ]]; then 00085 echo "import: missing module name argument!" 1>&2 00086 exit 1 00087 fi 00088 if [[ $# -gt 1 ]]; then 00089 echo "import: too many arguments!" 1>&2 00090 exit 1 00091 fi 00092 [[ ${module: -3} == '.sh' ]] || module="${module}.sh" 00093 local path="${BASHPATH}:" # ATTENTION: Trailing ':' required to terminate while loop! 00094 local root= 00095 while [[ -n "${path}" ]]; do 00096 root="${path%%:*}" 00097 if [[ "${root:0:1}" == '/' && -f "${root}/${module}" ]]; then 00098 . "${root}/${module}" 00099 return 0 00100 fi 00101 path="${path#*:}" 00102 done 00103 if [[ ${exitonerror} == 'true' ]]; then 00104 echo "import: module '$1' not found!" 1>&2 00105 exit 1 00106 fi 00107 return 1 00108 } 00109 00110 # ============================================================================ 00111 # pattern matching 00112 # ============================================================================ 00113 00114 # ---------------------------------------------------------------------------- 00115 ## @brief This function implements a more portable way to do pattern matching. 00116 # 00117 # Unfortunately, there are significant differences in the way patterns have 00118 # to be matched when using different shells. This function considers which 00119 # shell is used (at the moment only BASH), and uses the appropriate syntax 00120 # for the pattern matching. 00121 # 00122 # @param [in] value The string to match against pattern. 00123 # @param [in] pattern The pattern to match. 00124 # 00125 # @returns Whether the given string matches the given pattern. 00126 # 00127 # @retval 0 On match. 00128 # @retval 1 Otherwise. 00129 match() 00130 { 00131 [ $# -eq 2 ] || return 1 00132 00133 local value=$1 00134 local pattern=$2 00135 00136 if [ -z "${value}" ]; then 00137 [ -z "${pattern}" ] 00138 elif [ -z "${pattern}" ]; then 00139 [ -z "${value}" ] 00140 else 00141 if [ ${BASH_VERSION_MAJOR} -gt 2 ]; then 00142 # GNU bash, version 3.00.15(1)-release (x86_64-redhat-linux-gnu) 00143 # throws an error when a regular expression with groups 00144 # such as in '^(a|b|c)' is used. Here, quotes are required. 00145 if [ ${BASH_VERSION_MAJOR} -eq 3 -a ${BASH_VERSION_MINOR} -eq 0 ]; then 00146 [[ "${value}" =~ "${pattern}" ]] 00147 # GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu) 00148 # works with either quotes or not. However, on Mac OS Snow Leopard, 00149 # GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0) 00150 # requires that no quotes are used. The quotes are otherwise 00151 # considered to be part of the pattern. 00152 else 00153 [[ "${value}" =~ ${pattern} ]] 00154 fi 00155 else 00156 echo "${value}" | egrep -q "${pattern}" 00157 fi 00158 fi 00159 } 00160 00161 # ============================================================================ 00162 # upvar(s) 00163 # ============================================================================ 00164 00165 # ---------------------------------------------------------------------------- 00166 ## @brief Assign variable one scope above the caller. 00167 # 00168 # This function can be used inside functions to return values by assigning 00169 # them to a variable in the scope of the caller. 00170 # 00171 # @note For assigning multiple variables, use upvars(). Do NOT use multiple 00172 # upvar() calls, since one upvar() call might reassign a variable to 00173 # be used by another upvar() call. 00174 # 00175 # Example: 00176 # @code 00177 # foo () 00178 # { 00179 # local "$1" && upvar $1 "Hello, World!" 00180 # } 00181 # 00182 # foo greeting 00183 # echo ${greeting} 00184 # @endcode 00185 # 00186 # @param [in] var Variable name to assign value to 00187 # @param [in] values Value(s) to assign. If multiple values, an array is 00188 # assigned, otherwise a single value is assigned. 00189 # 00190 # @returns Nothing. 00191 # 00192 # @retval 0 On success. 00193 # @retval 1 On failure. 00194 # 00195 # @sa upvars() 00196 # @sa http://fvue.nl/wiki/Bash:_Passing_variables_by_reference 00197 upvar() 00198 { 00199 if unset -v "$1"; then # Unset & validate varname 00200 if (( $# == 2 )); then 00201 eval $1=\"\$2\" # Return single value 00202 else 00203 eval $1=\(\"\${@:2}\"\) # Return array 00204 fi 00205 fi 00206 } 00207 00208 # ---------------------------------------------------------------------------- 00209 ## @brief Assign variables one scope above the caller. 00210 # 00211 # @par Synopsis 00212 # local varname [varname ...] && 00213 # upvars [-v varname value] | [-aN varname [value ...]] ... 00214 # 00215 # @par Options: 00216 # - -aN Assign next N values to varname as array 00217 # - -v Assign single value to varname# 00218 # 00219 # @retval 0 On success. 00220 # @retval 1 On failure. 00221 # 00222 # @sa http://fvue.nl/wiki/Bash:_Passing_variables_by_reference 00223 upvars() 00224 { 00225 if ! (( $# )); then 00226 echo "${FUNCNAME[0]}: usage: ${FUNCNAME[0]} [-v varname"\ 00227 "value] | [-aN varname [value ...]] ..." 1>&2 00228 return 2 00229 fi 00230 while (( $# )); do 00231 case $1 in 00232 -a*) 00233 # Error checking 00234 [[ ${1#-a} ]] || { echo "bash: ${FUNCNAME[0]}: \`$1': missing"\ 00235 "number specifier" 1>&2; return 1; } 00236 printf %d "${1#-a}" &> /dev/null || { echo "bash:"\ 00237 "${FUNCNAME[0]}: \`$1': invalid number specifier" 1>&2 00238 return 1; } 00239 # Assign array of -aN elements 00240 [[ "$2" ]] && unset -v "$2" && eval $2=\(\"\${@:3:${1#-a}}\"\) && 00241 shift $((${1#-a} + 2)) || { echo "bash: ${FUNCNAME[0]}:"\ 00242 "\`$1${2+ }$2': missing argument(s)" 1>&2; return 1; } 00243 ;; 00244 -v) 00245 # Assign single value 00246 [[ "$2" ]] && unset -v "$2" && eval $2=\"\$3\" && 00247 shift 3 || { echo "bash: ${FUNCNAME[0]}: $1: missing"\ 00248 "argument(s)" 1>&2; return 1; } 00249 ;; 00250 --help) echo "\ 00251 Usage: local varname [varname ...] && 00252 ${FUNCNAME[0]} [-v varname value] | [-aN varname [value ...]] ... 00253 Available OPTIONS: 00254 -aN VARNAME [value ...] assign next N values to varname as array 00255 -v VARNAME value assign single value to varname 00256 --help display this help and exit 00257 --version output version information and exit" 00258 return 0 ;; 00259 --version) echo "\ 00260 ${FUNCNAME[0]}-0.9.dev 00261 Copyright (C) 2010 Freddy Vulto 00262 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> 00263 This is free software: you are free to change and redistribute it. 00264 There is NO WARRANTY, to the extent permitted by law." 00265 return 0 ;; 00266 *) 00267 echo "bash: ${FUNCNAME[0]}: $1: invalid option" 1>&2 00268 return 1 ;; 00269 esac 00270 done 00271 } 00272 00273 00274 ## @} 00275 # end of Doxygen group 00276 00277 00278 } # _BASIS_CORE_INCLUDED