From 5059f5f73bf60216c06d6ecd93f313588cce4105 Mon Sep 17 00:00:00 2001 From: Nathaniel Landau Date: Fri, 5 Nov 2021 20:38:47 -0400 Subject: [PATCH] Add new function and improve comments --- README.md | 1 + template.sh | 4 +-- template_standalone.sh | 4 +-- test/files.bats | 58 +++++++++++++++++++++++++++++++ test/fixtures/text.txt | 59 ++++++++++++++++++++++++++++++++ utilities/alerts.bash | 4 +-- utilities/arrays.bash | 2 +- utilities/debug.bash | 2 +- utilities/files.bash | 78 ++++++++++++++++++++++++++++++++++++++++++ utilities/strings.bash | 16 +++++---- 10 files changed, 213 insertions(+), 15 deletions(-) create mode 100644 test/fixtures/text.txt diff --git a/README.md b/README.md index f2425d6..45ac011 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,7 @@ Functions for working with files. - **`_listFiles_`** Find files in a directory. Use either glob or regex. - **`_makeSymlink_`** Creates a symlink and backs up a file which may be overwritten by the new symlink. If the exact same symlink already exists, nothing is done. - **`_parseYAML_`** Convert a YAML file into BASH variables for use in a shell script +- **`_printFileBetween_`** Prints block of text in a file between two regex patterns - **`_readFile_`** Prints each line of a file - **`_sourceFile_`** Source a file into a script - **`_createUniqueFilename_`** Ensure a file to be created has a unique filename to avoid overwriting other files diff --git a/template.sh b/template.sh index c08c415..7965c7b 100755 --- a/template.sh +++ b/template.sh @@ -18,8 +18,6 @@ _mainScript_() { #/_mainsScript_() # ################################## Flags and defaults -# Script specific - # # Required variables LOGFILE="${HOME}/logs/$(basename "$0").log" QUIET=false @@ -29,6 +27,8 @@ FORCE=false DRYRUN=false declare -a ARGS=() +# Script specific + # ################################## Functions required for this template to work _trapCleanup_() { diff --git a/template_standalone.sh b/template_standalone.sh index da4f287..aa6dc5b 100755 --- a/template_standalone.sh +++ b/template_standalone.sh @@ -17,8 +17,6 @@ _mainScript_() { # end _mainScript_ # ################################## Flags and defaults -# Script specific - # Required variables LOGFILE="${HOME}/logs/$(basename "$0").log" QUIET=false @@ -28,6 +26,8 @@ FORCE=false DRYRUN=false declare -a ARGS=() +# Script specific + # ################################## Custom utility functions (Pasted from repository) # ################################## Functions required for this template to work diff --git a/test/files.bats b/test/files.bats index 3b15699..37542fa 100755 --- a/test/files.bats +++ b/test/files.bats @@ -11,6 +11,8 @@ SOURCEFILE="${ROOTDIR}/utilities/files.bash" BASEHELPERS="${ROOTDIR}/utilities/misc.bash" ALERTS="${ROOTDIR}/utilities/alerts.bash" +PATH="/usr/local/opt/gnu-tar/libexec/gnubin:/usr/local/opt/coreutils/libexec/gnubin:/usr/local/opt/gnu-sed/libexec/gnubin:/usr/local/opt/grep/libexec/gnubin:${PATH}" + if test -f "${SOURCEFILE}" >&2; then source "${SOURCEFILE}" else @@ -68,6 +70,7 @@ teardown() { } ######## FIXTURES ######## +TEXT="${BATS_TEST_DIRNAME}/fixtures/text.txt" YAML1="${BATS_TEST_DIRNAME}/fixtures/yaml1.yaml" YAML1parse="${BATS_TEST_DIRNAME}/fixtures/yaml1.yaml.txt" unencrypted="${BATS_TEST_DIRNAME}/fixtures/test.md" @@ -433,6 +436,61 @@ _testParseYAML_() { assert_success } +@test "_printFileBetween_: match case-insensitive" { + run _printFileBetween_ -i "^#+ orange1" "^#+$" "${TEXT}" + assert_success + assert_line --index 0 "############ Orange1 ############" + assert_line --index 1 "# 1" + assert_line --index 2 "# 2" + assert_line --index 3 "# 3" + assert_line --index 4 "# 4" + assert_line --index 5 "#################################" + refute_output --regexp "Grape|Orange2" +} + +@test "_printFileBetween_: match case-insensitive - greedy" { + run _printFileBetween_ -ig "^#+ orange" "##" "${TEXT}" + assert_success + assert_line --index 0 "############ Orange1 ############" + assert_line --index 1 "# 1" + assert_line --index 2 "# 2" + assert_line --index 3 "# 3" + assert_line --index 4 "# 4" + assert_line --index 5 "#################################" + assert_line --index 6 "############ Orange2 ############" + assert_line --index 7 "# 1" + assert_line --index 8 "# 2" + assert_line --index 9 "# 3" + assert_line --index 10 "# 4" + assert_line --index 11 "#################################" + refute_output --regexp "Grape" +} + +@test "_printFileBetween_: no match" { + run _printFileBetween_ "^#+ orange1" "^#+$" "${TEXT}" + assert_failure +} + +@test "_printFileBetween_: remove lines" { + run _printFileBetween_ -ri "^[A-Z0-9]+\(\)" "^ *}.*" "${TEXT}" + assert_success + assert_line --index 0 --partial "# buf : Backup file with time stamp" + assert_line --index 5 --regexp "^ *cp -a .*" + refute_output --regexp "buf\(\) {" + refute_output --regexp '}[^"_]' + refute_output --regexp "md5Check" +} + +@test "_printFileBetween_: remove lines - greedy" { + run _printFileBetween_ -gr "^[a-zA-Z0-9]+\(\)" "^ *}.*" "${TEXT}" + assert_success + assert_line --index 0 --partial "# buf : Backup file with time stamp" + assert_line --index 5 --regexp "^ *cp -a .*" + refute_output --regexp "buf\(\) {" + assert_line --index 29 --regexp "^ *fi.*" + assert_output --regexp "md5Check" +} + _testBackupFile_ _testListFiles_ _testMakeSymlink_ diff --git a/test/fixtures/text.txt b/test/fixtures/text.txt new file mode 100644 index 0000000..064ebf2 --- /dev/null +++ b/test/fixtures/text.txt @@ -0,0 +1,59 @@ +############ Orange1 ############ +# 1 +# 2 +# 3 +# 4 +################################# + +############ Orange2 ############ +# 1 +# 2 +# 3 +# 4 +################################# + +############ Grape ############ +# 1 +# 2 +# 3 +# 4 +################################# + +buf() { + # buf : Backup file with time stamp + local filename + local filetime + + filename="${1}" + filetime=$(date +%Y%m%d_%H%M%S) + cp -a "${filename}" "${filename}_${filetime}" +} + +md5Check() { + # DESC: Compares an md5 hash to the md5 hash of a file + # ARGS: None + # OUTS: None + # USAGE: md5Check + + local opt + local OPTIND=1 + local md5="$1" + local file="$2" + + if ! command -v md5sum &>/dev/null; then + echo "Can not find 'md5sum' utility" + return 1 + fi + + # Get md5 has of file + local filemd5 + filemd5="$(md5sum "${file}")" + + if [[ $filemd5 == "$md5" ]]; then + success "The two md5 hashes match" + return 0 + else + warning "The two md5 hashes do not match" + return 1 + fi +} diff --git a/utilities/alerts.bash b/utilities/alerts.bash index b926bcc..0ab4e02 100644 --- a/utilities/alerts.bash +++ b/utilities/alerts.bash @@ -275,9 +275,9 @@ _columnizeOutput_() { # NOTE: # Long text or ANSI colors in the first column may create display issues # USAGE: - # _columnizeOutput_ 0 30 10 "Key" "Long value text" + # _columnizeOutput_ 0 30 "Key" "Long value text" - [[ $# -lt 5 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" + [[ $# -lt 4 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" local _leftIndent=$1 local _leftColumn=$2 diff --git a/utilities/arrays.bash b/utilities/arrays.bash index c57476f..2dfc469 100644 --- a/utilities/arrays.bash +++ b/utilities/arrays.bash @@ -8,7 +8,7 @@ _dedupeArray_() { # OUTS: # stdout: Prints de-duped elements # USAGE: - # _removeDups_ "${array[@]}" + # mapfile -t newarray < <(_dedupeArray_ "${array[@]}") # NOTE: # List order may not stay the same # CREDIT: diff --git a/utilities/debug.bash b/utilities/debug.bash index 96ca3bc..26adaec 100644 --- a/utilities/debug.bash +++ b/utilities/debug.bash @@ -58,7 +58,7 @@ _printArray_() { [[ ${VERBOSE:-} != true ]] && return 0 - debug "Printing contents of \${${_arrayName}[@]}" "${_lineNumber}" + debug "Contents of \${${_arrayName}[@]}" "${_lineNumber}" for _k in "${!_arr[@]}"; do debug "${_k} = ${_arr[${_k}]}" diff --git a/utilities/files.bash b/utilities/files.bash index f26a1b4..2364594 100644 --- a/utilities/files.bash +++ b/utilities/files.bash @@ -671,6 +671,84 @@ _parseYAML_() { }' | sed 's/_=/+=/g' | sed 's/[[:space:]]*#.*"/"/g' } +_printFileBetween_() ( + # DESC: + # Prints text of a file between two regex patterns + # ARGS: + # $1 (Required): Starting regex pattern + # $2 (Required): Ending regex pattern + # $3 (Required): Input string + # OPTIONS: + # -i (Optional) - Case-insensitive regex + # -r (Optional) - Remove first and last lines (ie - the lines which matched the patterns) + # -g (Optional) - Greedy regex (Defaults to non-greedy) + # OUTS: + # 0: Success + # 1: Failure + # stdout: Prints text between two regex patterns + # USAGE: + # _printFileBetween_ "^pattern1$" "^pattern2$" "String or variable containing a string" + + [[ $# -lt 3 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" + + local _removeLines=false + local _greedy=false + local _caseInsensitive=false + local opt + local OPTIND=1 + while getopts ":iIrRgG" opt; do + case ${opt} in + i | I) _caseInsensitive=true ;; + r | R) _removeLines=true ;; + g | G) _greedy=true ;; + *) fatal "Unrecognized option '${1}' passed to ${FUNCNAME[0]}. Exiting." ;; + esac + done + shift $((OPTIND - 1)) + + local _startRegex="${1}" + local _endRegex="${2}" + local _input="${3}" + local _output + + if [[ ${_removeLines} == true ]]; then + if [[ ${_greedy} == true ]]; then + if [[ ${_caseInsensitive} == true ]]; then + _output="$(sed -nE "/${_startRegex}/I,/${_endRegex}/Ip" "${_input}" | sed -n '2,$p' | sed '$d')" + else + _output="$(sed -nE "/${_startRegex}/,/${_endRegex}/p" "${_input}" | sed -n '2,$p' | sed '$d')" + fi + else + if [[ ${_caseInsensitive} == true ]]; then + _output="$(sed -nE "/${_startRegex}/I,/${_endRegex}/I{p;/${_endRegex}/Iq}" "${_input}" | sed -n '2,$p' | sed '$d')" + else + _output="$(sed -nE "/${_startRegex}/,/${_endRegex}/{p;/${_endRegex}/q}" "${_input}" | sed -n '2,$p' | sed '$d')" + fi + fi + else + if [[ ${_greedy} == true ]]; then + if [[ ${_caseInsensitive} == true ]]; then + _output="$(sed -nE "/${_startRegex}/I,/${_endRegex}/Ip" "${_input}")" + else + _output="$(sed -nE "/${_startRegex}/,/${_endRegex}/p" "${_input}")" + fi + else + if [[ ${_caseInsensitive} == true ]]; then + _output="$(sed -nE "/${_startRegex}/I,/${_endRegex}/I{p;/${_endRegex}/Iq}" "${_input}")" + else + _output="$(sed -nE "/${_startRegex}/,/${_endRegex}/{p;/${_endRegex}/q}" "${_input}")" + fi + fi + fi + + if [[ -n ${_output:-} ]]; then + printf "%s\n" "${_output}" + return 0 + else + return 1 + fi +) + _readFile_() { # DESC: # Prints each line of a file diff --git a/utilities/strings.bash b/utilities/strings.bash index 33a75fb..8054cab 100644 --- a/utilities/strings.bash +++ b/utilities/strings.bash @@ -243,7 +243,7 @@ _regexCapture_() { # 1 - Regex did not match # stdout: Prints string matching regex # USAGE: - # HEXCODE=$(_regex_ "background-color: #FFFFFF;" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$') + # HEXCODE=$(_regexCapture_ "background-color: #FFFFFF;" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$') # $ printf "%s\n" "${HEXCODE}" # $ #FFFFFF # NOTE: @@ -290,9 +290,9 @@ _rtrim_() { sed "s%[${_char//%/\\%}]*$%%" } -_splitString_() { +_splitString_() ( # DESC: - # Splat a string into an array based on a given delimiter + # Split a string into an array based on a given delimiter # ARGS: # $1 (Required) - String to be split # $2 (Required) - Delimiter @@ -302,15 +302,17 @@ _splitString_() { # stdout: Values split by delimiter separated by newline # USAGE: # ARRAY=( $(_splitString_ "string1,string2,string3" ",") ) - # CREDIT: - # https://github.com/labbots/bash-utility/blob/master/src/misc.sh [[ $# -lt 2 ]] && fatal "Missing required argument to ${FUNCNAME[0]}" declare -a _arr=() - IFS=$'\n' read -d "" -ra _arr <<<"${1//$2/$'\n'}" + local _input="${1}" + local _delimeter="${2}" + + IFS="${_delimeter}" read -r -a _arr <<<"${_input}" + printf '%s\n' "${_arr[@]}" -} +) _stringContains_() { # DESC: