utils.sh.inc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. # bash utils from angularjs
  2. # This file provides:
  3. # - a default control flow
  4. # * initializes the environment
  5. # * call a function in your script based on the arguments
  6. # - named argument parsing and automatic generation of the "usage" for your script
  7. # - utility functions
  8. #
  9. # Usage:
  10. # - define the variable ARGS_DEF (see below) with the arguments for your script
  11. # - include this file using `source utils.inc` at the end of your script.
  12. #
  13. # Default control flow:
  14. # 0. Set the current directory to the directory of the script. By this
  15. # the script can be called from anywhere.
  16. # 1. Parse the named arguments
  17. # 2. [Redacted]
  18. # 3. If the parameter "verbose" is set, the `-x` flag will be set in bash.
  19. # 4. The function "init" will be called if it exists
  20. # 5. If the parameter "action" is set, it will call the function with the name of that parameter.
  21. # Otherwise the function "run" will be called.
  22. #
  23. # Named Argument Parsing:
  24. # - The variable ARGS_DEF defines the valid command arguments
  25. # * Required args syntax: --paramName=paramRegex
  26. # * Optional args syntax: [--paramName=paramRegex]
  27. # * e.g. ARG_DEFS=("--required_param=(.+)" "[--optional_param=(.+)]")
  28. # - Checks that:
  29. # * all arguments match to an entry in ARGS_DEF
  30. # * all required arguments are present
  31. # * all arguments match their regex
  32. # - Afterwards, every paramter value will be stored in a variable
  33. # with the name of the parameter in upper case (with dash converted to underscore).
  34. #
  35. # Special arguments that are always available:
  36. # - "--action=.*": This parameter will be used to dispatch to a function with that name when the
  37. # script is started
  38. # - "--verbose=true": This will set the `-x` flag in bash so that all calls will be logged
  39. #
  40. # Utility functions:
  41. # - readJsonProp
  42. # - replaceJsonProp
  43. # - resolveDir
  44. # - getVar
  45. # - serVar
  46. # - isFunction
  47. # always stop on errors
  48. set -e
  49. function usage {
  50. echo "Usage: ${0} ${ARG_DEFS[@]}"
  51. exit 1
  52. }
  53. function parseArgs {
  54. local REQUIRED_ARG_NAMES=()
  55. # -- helper functions
  56. function varName {
  57. # everything to upper case and dash to underscore
  58. echo ${1//-/_} | tr '[:lower:]' '[:upper:]'
  59. }
  60. function readArgDefs {
  61. local ARG_DEF
  62. local AD_OPTIONAL
  63. local AD_NAME
  64. local AD_RE
  65. # -- helper functions
  66. function parseArgDef {
  67. local ARG_DEF_REGEX="(\[?)--([^=]+)=(.*)"
  68. if [[ ! $1 =~ $ARG_DEF_REGEX ]]; then
  69. echo "Internal error: arg def has wrong format: $ARG_DEF"
  70. exit 1
  71. fi
  72. AD_OPTIONAL="${BASH_REMATCH[1]}"
  73. AD_NAME="${BASH_REMATCH[2]}"
  74. AD_RE="${BASH_REMATCH[3]}"
  75. if [[ $AD_OPTIONAL ]]; then
  76. # Remove last bracket for optional args.
  77. # Can't put this into the ARG_DEF_REGEX somehow...
  78. AD_RE=${AD_RE%?}
  79. fi
  80. }
  81. # -- run
  82. for ARG_DEF in "${ARG_DEFS[@]}"
  83. do
  84. parseArgDef $ARG_DEF
  85. local AD_NAME_UPPER=$(varName $AD_NAME)
  86. setVar "${AD_NAME_UPPER}_OPTIONAL" "$AD_OPTIONAL"
  87. setVar "${AD_NAME_UPPER}_RE" "$AD_RE"
  88. if [[ ! $AD_OPTIONAL ]]; then
  89. REQUIRED_ARG_NAMES+=($AD_NAME)
  90. fi
  91. done
  92. }
  93. function readAndValidateArgs {
  94. local ARG_NAME
  95. local ARG_VALUE
  96. local ARG_NAME_UPPER
  97. # -- helper functions
  98. function parseArg {
  99. local ARG_REGEX="--([^=]+)=?(.*)"
  100. if [[ ! $1 =~ $ARG_REGEX ]]; then
  101. echo "Can't parse argument $i"
  102. usage
  103. fi
  104. ARG_NAME="${BASH_REMATCH[1]}"
  105. ARG_VALUE="${BASH_REMATCH[2]}"
  106. ARG_NAME_UPPER=$(varName $ARG_NAME)
  107. }
  108. function validateArg {
  109. local AD_RE=$(getVar ${ARG_NAME_UPPER}_RE)
  110. if [[ ! $AD_RE ]]; then
  111. echo "Unknown option: $ARG_NAME"
  112. usage
  113. fi
  114. if [[ ! $ARG_VALUE =~ ^${AD_RE}$ ]]; then
  115. echo "Wrong format: $ARG_NAME"
  116. usage;
  117. fi
  118. # validate that the "action" option points to a valid function
  119. if [[ $ARG_NAME == "action" ]] && ! isFunction $ARG_VALUE; then
  120. echo "No action $ARG_VALUE defined in this script"
  121. usage;
  122. fi
  123. }
  124. # -- run
  125. for i in "$@"
  126. do
  127. parseArg $i
  128. validateArg
  129. setVar "${ARG_NAME_UPPER}" "$ARG_VALUE"
  130. done
  131. }
  132. function checkMissingArgs {
  133. local ARG_NAME
  134. for ARG_NAME in "${REQUIRED_ARG_NAMES[@]}"
  135. do
  136. ARG_VALUE=$(getVar $(varName $ARG_NAME))
  137. if [[ ! $ARG_VALUE ]]; then
  138. echo "Missing: $ARG_NAME"
  139. usage;
  140. fi
  141. done
  142. }
  143. # -- run
  144. readArgDefs
  145. readAndValidateArgs "$@"
  146. checkMissingArgs
  147. }
  148. # getVar(varName)
  149. function getVar {
  150. echo ${!1}
  151. }
  152. # setVar(varName, varValue)
  153. function setVar {
  154. eval "$1=\"$2\""
  155. }
  156. # isFunction(name)
  157. # - to be used in an if, so return 0 if successful and 1 if not!
  158. function isFunction {
  159. if [[ $(type -t $1) == "function" ]]; then
  160. return 0
  161. else
  162. return 1
  163. fi
  164. }
  165. # readJsonProp(jsonFile, property)
  166. # - restriction: property needs to be on an own line!
  167. function readJsonProp {
  168. echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1)
  169. }
  170. # replaceJsonProp(jsonFile, property, newValue)
  171. # - note: propertyRegex will be automatically placed into a
  172. # capturing group! -> all other groups start at index 2!
  173. function replaceJsonProp {
  174. replaceInFile $1 "\"$2\": \".*?\"" "\"$2\": \"$3\""
  175. }
  176. # replaceInFile(file, findPattern, replacePattern)
  177. function replaceInFile {
  178. perl -pi -e "s/$2/$3/g;" $1
  179. }
  180. # resolveDir(relativeDir)
  181. # - resolves a directory relative to the current script
  182. function resolveDir {
  183. echo $(cd $SCRIPT_DIR; cd $1; pwd)
  184. }
  185. function main {
  186. # normalize the working dir to the directory of the script
  187. cd $(dirname $0);SCRIPT_DIR=$(pwd)
  188. ARG_DEFS+=("[--verbose=(true|false)]")
  189. parseArgs "$@"
  190. # --verbose argument
  191. if [[ $VERBOSE == "true" ]]; then
  192. set -x
  193. fi
  194. if isFunction init; then
  195. init "$@"
  196. fi
  197. # jump to the function denoted by the --action argument,
  198. # otherwise call the "run" function
  199. if [[ $ACTION ]]; then
  200. $ACTION "$@"
  201. else
  202. run "$@"
  203. fi
  204. }
  205. main "$@"