@renault-digital/bash-base 2.4.1 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/reflection.sh CHANGED
@@ -13,15 +13,50 @@
13
13
  # string="args_valid_or_read myVar '^[0-9a-z]{3,3}$' \"SIA\""
14
14
  # reflect_nth_arg 4 $string ==> "SIA"
15
15
  # @SEE_ALSO
16
- function reflect_nth_arg() {
17
- local index string args
16
+ reflect_nth_arg() {
17
+ local index string word position
18
18
 
19
- string="$(echo "$@" | string_replace_regex '(\\|\||\{|>|<|&)' '\\\1')"
20
- args=()
21
- eval 'for word in '"${string}"'; do args+=("$word"); done'
19
+ if [[ -n "${ZSH_VERSION:-}" ]]; then
20
+ # zsh's lexer splits shell-quoted words without executing their contents.
21
+ # Keep this expansion in eval so Bash can still parse the source file.
22
+ string="$*"
23
+ local -a __bash_base_reflect_words
24
+ eval '__bash_base_reflect_words=( "${(@Q)${(z)string}}" )' || return 1
25
+ set -- "${__bash_base_reflect_words[@]}"
26
+ else
27
+ string="$(printf '%s\n' "$*" | string_replace_regex '(\\|\||\{|>|<|&)' '\\\1')"
22
28
 
23
- index="${args[0]}"
24
- echo "${args[$index]}"
29
+ # Parse the same shell-like argument string as the historical implementation,
30
+ # but use positional parameters instead of an indexed array so Bash/zsh array
31
+ # index bases cannot change the result.
32
+ eval "set -- ${string}"
33
+ fi
34
+ index="${1-}"
35
+ if [[ -n "${BASH_VERSION:-}" ]]; then
36
+ # Bash legacy indexing addressed the entire parsed list, including
37
+ # the index argument at [0]; missing indexes printed a blank line.
38
+ # Keep that behavior, including Bash's native negative indexes.
39
+ local -a __bash_base_legacy_reflect_words
40
+ __bash_base_legacy_reflect_words=("$@")
41
+ echo "${__bash_base_legacy_reflect_words[$index]}"
42
+ return 0
43
+ fi
44
+ shift || true
45
+
46
+ if ! [[ "$index" =~ ^[0-9]+$ ]] || ((index < 1)); then
47
+ return 1
48
+ fi
49
+
50
+ position=1
51
+ for word in "$@"; do
52
+ if ((position == index)); then
53
+ printf '%s\n' "$word"
54
+ return 0
55
+ fi
56
+ position=$((position + 1))
57
+ done
58
+
59
+ return 1
25
60
  }
26
61
 
27
62
  # @NAME
@@ -34,9 +69,9 @@ function reflect_nth_arg() {
34
69
  # reflect_get_function_definition confirm_to_continue
35
70
  # @SEE_ALSO
36
71
  # reflect_function_names_of_file
37
- function reflect_get_function_definition() {
72
+ reflect_get_function_definition() {
38
73
  local functionName="$1"
39
- declare -f "$functionName"
74
+ typeset -f "$functionName"
40
75
  }
41
76
 
42
77
  # @NAME
@@ -50,9 +85,14 @@ function reflect_get_function_definition() {
50
85
  # reflect_function_names_of_file scripts/my_script.sh
51
86
  # @SEE_ALSO
52
87
  # reflect_get_function_definition
53
- function reflect_function_names_of_file() {
88
+ reflect_function_names_of_file() {
54
89
  local shellScriptFile="$1"
55
- grep -E '^[[:space:]]*(function)?[[:space:]]*[0-9A-Za-z_\-]+[[:space:]]*\(\)[[:space:]]*\{?' "${shellScriptFile}" | cut -d'(' -f1 | sed -e "s/function//"
90
+ if [[ -n "${BASH_VERSION:-}" ]]; then
91
+ # Existing Bash callers may rely on the original output spelling.
92
+ grep -E '^[[:space:]]*(function)?[[:space:]]*[0-9A-Za-z_\-]+[[:space:]]*\(\)[[:space:]]*\{?' "$shellScriptFile" | cut -d'(' -f1 | sed -e 's/function//'
93
+ return
94
+ fi
95
+ grep -E '^[[:space:]]*(function[[:space:]]+)?[0-9A-Za-z_-]+[[:space:]]*\(\)[[:space:]]*\{?' "$shellScriptFile" | sed -E -e 's/^[[:space:]]*function[[:space:]]+//' -e 's/[[:space:]]*\(\).*//' -e 's/[[:space:]]+$//'
56
96
  }
57
97
 
58
98
  # @NAME
@@ -66,9 +106,13 @@ function reflect_function_names_of_file() {
66
106
  # reflect_function_definitions_of_file scripts/my_script.sh
67
107
  # @SEE_ALSO
68
108
  # reflect_get_function_definition
69
- function reflect_function_definitions_of_file() {
109
+ reflect_function_definitions_of_file() {
70
110
  local shellScriptFile="$1"
71
- sed -E -n '/^[[:space:]]*(function)?[[:space:]]*[0-9A-Za-z_\-]+[[:space:]]*\(\)[[:space:]]*\{?/,/^[[:space:]]*}/p' "${shellScriptFile}"
111
+ if [[ -n "${BASH_VERSION:-}" ]]; then
112
+ sed -E -n '/^[[:space:]]*(function)?[[:space:]]*[0-9A-Za-z_\-]+[[:space:]]*\(\)[[:space:]]*\{?/,/^[[:space:]]*}/p' "$shellScriptFile"
113
+ return
114
+ fi
115
+ sed -E -n '/^[[:space:]]*(function[[:space:]]+)?[0-9A-Za-z_-]+[[:space:]]*\(\)[[:space:]]*\{?/,/^[[:space:]]*}/p' "$shellScriptFile"
72
116
  }
73
117
 
74
118
  # @NAME
@@ -82,9 +126,17 @@ function reflect_function_definitions_of_file() {
82
126
  # reflect_search_function '^args_.*'
83
127
  # @SEE_ALSO
84
128
  # reflect_search_variable
85
- function reflect_search_function() {
129
+ reflect_search_function() {
86
130
  local functionNamePattern="$1"
87
- declare -f | grep -E '\s+\(\)\s+' | sed -E 's/[(){ ]//g' | grep -Ei "${functionNamePattern}"
131
+
132
+ if [[ -n "${BASH_VERSION:-}" ]]; then
133
+ # Exact Bash legacy extraction/output (no new sorting or dedupe).
134
+ typeset -f | grep -E '\s+\(\)\s+' | sed -E 's/[(){ ]//g' | grep -Ei "$functionNamePattern"
135
+ elif [[ -n "${ZSH_VERSION:-}" ]]; then
136
+ # Hide zsh-only parameter-expansion flags inside eval so the file still
137
+ # parses in Bash.
138
+ eval 'printf "%s\\n" "${(k)functions[@]}"' | LC_ALL=C sort -u | grep -Ei "$functionNamePattern"
139
+ fi
88
140
  }
89
141
 
90
142
  # @NAME
@@ -98,7 +150,14 @@ function reflect_search_function() {
98
150
  # reflect_search_variable '^COLOR'
99
151
  # @SEE_ALSO
100
152
  # reflect_search_function
101
- function reflect_search_variable() {
153
+ reflect_search_variable() {
102
154
  local variableNamePattern="$1"
103
- declare -p | grep -Eo '\s+\w+=' | sed -E 's/[= ]//g' | grep -Ei "${variableNamePattern}"
155
+
156
+ if [[ -n "${BASH_VERSION:-}" ]]; then
157
+ # Enumerate Bash variables directly. Parsing typeset -p using \s/\w
158
+ # depends on grep extensions unavailable on some macOS versions.
159
+ compgen -A variable | grep -Ei "$variableNamePattern"
160
+ elif [[ -n "${ZSH_VERSION:-}" ]]; then
161
+ eval 'printf "%s\\n" "${(k)parameters[@]}"' | LC_ALL=C sort -u | grep -Ei "$variableNamePattern"
162
+ fi
104
163
  }
package/src/string.sh CHANGED
@@ -13,7 +13,7 @@ source src/tool.sh
13
13
  # string_trim < logfile
14
14
  # echo " add " | string_trim
15
15
  # @SEE_ALSO
16
- function string_trim() {
16
+ string_trim() {
17
17
  local string="${1-$(cat)}"
18
18
  echo "${string}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' #trim ' '
19
19
  }
@@ -29,7 +29,7 @@ function string_trim() {
29
29
  # string_repeat 'abc' 5
30
30
  # echo 5 | string_repeat 'abc'
31
31
  # @SEE_ALSO
32
- function string_repeat() {
32
+ string_repeat() {
33
33
  local string="$1"
34
34
  local nbTimes=${2-$(cat)}
35
35
  printf "${string}%.0s" $(seq 1 "$nbTimes")
@@ -46,7 +46,7 @@ function string_repeat() {
46
46
  # string_length < logfile
47
47
  # echo " add " | string_length
48
48
  # @SEE_ALSO
49
- function string_length() {
49
+ string_length() {
50
50
  local string index
51
51
  string="${1-$(cat)}"
52
52
 
@@ -66,7 +66,7 @@ function string_length() {
66
66
  # echo " add " | string_is_empty
67
67
  # @SEE_ALSO
68
68
  # string_length
69
- function string_is_empty() {
69
+ string_is_empty() {
70
70
  local string="${1-$(cat)}"
71
71
 
72
72
  [[ $(string_length "$string") -eq 0 ]]
@@ -82,7 +82,7 @@ function string_is_empty() {
82
82
  # string_revert 'aBc'
83
83
  # echo 'aBc' | string_revert
84
84
  # @SEE_ALSO
85
- function string_revert() {
85
+ string_revert() {
86
86
  local string="${1-$(cat)}"
87
87
  echo "${string}" | rev
88
88
  }
@@ -98,9 +98,16 @@ function string_revert() {
98
98
  # echo 'abc' | string_upper
99
99
  # @SEE_ALSO
100
100
  # string_upper_first, string_lower
101
- function string_upper() {
101
+ string_upper() {
102
102
  local string=${1-$(cat)}
103
- echo "${string^^}"
103
+ if [[ -n "${BASH_VERSION:-}" ]] && ((BASH_VERSINFO[0] >= 4)); then
104
+ # Bash's case expansion is locale-aware (unlike many tr versions).
105
+ # Keep the Bash 4+/5+ echo behavior verbatim. Construct the expression
106
+ # at runtime so Bash 3.2 can source this file without evaluating it.
107
+ eval 'echo "${string'"^^"'}"'
108
+ return
109
+ fi
110
+ printf '%s\n' "$string" | tr '[:lower:]' '[:upper:]'
104
111
  }
105
112
 
106
113
  # @NAME
@@ -114,9 +121,13 @@ function string_upper() {
114
121
  # echo 'aBc' | string_lower
115
122
  # @SEE_ALSO
116
123
  # string_upper, string_upper_first
117
- function string_lower() {
124
+ string_lower() {
118
125
  local string=${1-$(cat)}
119
- echo "${string,,}"
126
+ if [[ -n "${BASH_VERSION:-}" ]] && ((BASH_VERSINFO[0] >= 4)); then
127
+ eval 'echo "${string'",,"'}"'
128
+ return
129
+ fi
130
+ printf '%s\n' "$string" | tr '[:upper:]' '[:lower:]'
120
131
  }
121
132
 
122
133
  # @NAME
@@ -130,10 +141,29 @@ function string_lower() {
130
141
  # echo 'aBc' | string_upper_first
131
142
  # @SEE_ALSO
132
143
  # string_lower, string_upper
133
- function string_upper_first() {
144
+ string_upper_first() {
134
145
  local string=${1-$(cat)}
135
- local lower="${string,,}"
136
- echo "${lower^}"
146
+ local lower first rest
147
+
148
+ if [[ -n "${BASH_VERSION:-}" ]] && ((BASH_VERSINFO[0] >= 4)); then
149
+ # Do not round-trip through command substitution: it strips the
150
+ # trailing newlines that the old Bash parameter expansion preserved.
151
+ eval 'lower="${string'",,"'}"'
152
+ eval 'echo "${lower'"^"'}"'
153
+ return
154
+ fi
155
+
156
+ lower="$(string_lower "$string")"
157
+ [[ -n "$lower" ]] || {
158
+ printf '\n'
159
+ return 0
160
+ }
161
+
162
+ # `${var^^}` / `${var,,}` / `${var^}` are Bash-specific. Parameter
163
+ # prefix/suffix removal is shared by Bash and zsh.
164
+ rest="${lower#?}"
165
+ first="${lower%"$rest"}"
166
+ printf '%s%s\n' "$(string_upper "$first")" "$rest"
137
167
  }
138
168
 
139
169
  # @NAME
@@ -149,11 +179,37 @@ function string_upper_first() {
149
179
  # string_sub 3 5 < temp_file.txt
150
180
  # echo ' as fd ' | string_sub 2 4
151
181
  # @SEE_ALSO
152
- function string_sub() {
182
+ string_sub() {
153
183
  local startIndex=$1
154
184
  local subStringLength=$2
155
185
  local string="${3-$(cat)}"
156
- echo "${string:$startIndex:$subStringLength}"
186
+ if [[ -n "${BASH_VERSION:-}" ]] && ((BASH_VERSINFO[0] >= 4)); then
187
+ # Keep Bash 4+/5+ substring and error behavior exactly as before.
188
+ # Bash 3.2 continues to use the portable negative-length fallback.
189
+ echo "${string:$startIndex:$subStringLength}"
190
+ return
191
+ fi
192
+ local totalLength=${#string}
193
+ local actualStart=$startIndex
194
+ local actualLength=$subStringLength
195
+
196
+ # Bash 3.2 cannot use a negative substring length. Convert the
197
+ # documented negative start/end offsets to nonnegative bounds first.
198
+ if ((actualStart < 0)); then
199
+ actualStart=$((totalLength + actualStart))
200
+ fi
201
+ if ((actualLength < 0)); then
202
+ actualLength=$((totalLength + actualLength - actualStart))
203
+ fi
204
+ # Out-of-range starts produce an empty result (as in modern Bash).
205
+ if ((actualStart < 0 || actualStart > totalLength)); then
206
+ printf '\n'
207
+ return 0
208
+ fi
209
+ if ((actualLength < 0)); then
210
+ return 1
211
+ fi
212
+ printf '%s\n' "${string:$actualStart:$actualLength}"
157
213
  }
158
214
 
159
215
  # @NAME
@@ -167,7 +223,7 @@ function string_sub() {
167
223
  # string_match 'name;+' "name;name;"
168
224
  # @SEE_ALSO
169
225
  # string_index_first
170
- function string_match() {
226
+ string_match() {
171
227
  local value regExp
172
228
  regExp=${1}
173
229
  string="${2-$(cat)}"
@@ -185,10 +241,14 @@ function string_match() {
185
241
  # escape_sed 'a$'
186
242
  # @SEE_ALSO
187
243
  # string_replace
188
- function escape_sed() {
189
- echo "${1}" | sed -e 's/\//\\\//g' -e 's/\&/\\\&/g' -e 's/\./\\\./g' -e 's/\^/\\\^/g' -e 's/\[/\\\[/g' -e 's/\$/\\\$/g'
244
+ escape_sed() {
245
+ if [[ -n "${ZSH_VERSION:-}" ]]; then
246
+ printf '%s\n' "$1"
247
+ else
248
+ # Preserve historical Bash echo semantics.
249
+ echo "$1"
250
+ fi | sed -e 's/\//\\\//g' -e 's/\&/\\\&/g' -e 's/\./\\\./g' -e 's/\^/\\\^/g' -e 's/\[/\\\[/g' -e 's/\$/\\\$/g'
190
251
  }
191
- export -f escape_sed >/dev/null
192
252
 
193
253
  # @NAME
194
254
  # string_replace -- replace literally the token string to new string, not support regular expression
@@ -204,11 +264,16 @@ export -f escape_sed >/dev/null
204
264
  # string_replace '\*' 'b' 'a*a' ==> 'aba'
205
265
  # @SEE_ALSO
206
266
  # escape_sed, string_replace_regex
207
- function string_replace() {
267
+ string_replace() {
208
268
  local tokenString newString
209
269
  tokenString=$(escape_sed "${1}")
210
270
  newString=$(escape_sed "${2}")
211
- echo "${3-$(cat)}" | sed -e "s/${tokenString}/${newString}/g"
271
+ if [[ -n "${ZSH_VERSION:-}" ]]; then
272
+ printf '%s\n' "${3-$(cat)}" | sed -e "s/${tokenString}/${newString}/g"
273
+ else
274
+ # Preserve historical Bash echo semantics.
275
+ echo "${3-$(cat)}" | sed -e "s/${tokenString}/${newString}/g"
276
+ fi
212
277
  }
213
278
 
214
279
  # @NAME
@@ -225,7 +290,7 @@ function string_replace() {
225
290
  # string_replace_regex '*' 'b' 'a*a' ==> 'aba'
226
291
  # @SEE_ALSO
227
292
  # string_replace
228
- function string_replace_regex() {
293
+ string_replace_regex() {
229
294
  echo "${3-$(cat)}" | sed -E -e "s/$1/$2/g"
230
295
  }
231
296
 
@@ -242,11 +307,11 @@ function string_replace_regex() {
242
307
  # echo " add " | string_index_first "token"
243
308
  # @SEE_ALSO
244
309
  # string_before_first, string_after_first
245
- function string_index_first() {
310
+ string_index_first() {
246
311
  local tokenString=$1
247
312
  local string="${2-$(cat)}"
248
313
  local prefix="${string%%${tokenString}*}"
249
- [ "${string}" == "${prefix}" ] && echo -1 || echo ${#prefix}
314
+ [[ "${string}" == "${prefix}" ]] && printf '%s\n' -1 || printf '%s\n' "${#prefix}"
250
315
  }
251
316
 
252
317
  # @NAME
@@ -262,7 +327,7 @@ function string_index_first() {
262
327
  # echo " add " | string_before_first "dd"
263
328
  # @SEE_ALSO
264
329
  # string_index_first, string_after_first
265
- function string_before_first() {
330
+ string_before_first() {
266
331
  local tokenString=$1
267
332
  local string="${2-$(cat)}"
268
333
  echo "${string%%${tokenString}*}" # Remove the first - and everything following it
@@ -281,7 +346,7 @@ function string_before_first() {
281
346
  # echo " add " | string_after_first "dd"
282
347
  # @SEE_ALSO
283
348
  # string_index_first, string_before_first
284
- function string_after_first() {
349
+ string_after_first() {
285
350
  local tokenString=$1
286
351
  local string="${2-$(cat)}"
287
352
  echo "${string#*${tokenString}}" # Remove everything up to and including first -
@@ -303,7 +368,7 @@ function string_after_first() {
303
368
  # string_split_to_array $'\n' branchesToSelectArray "${branchesToSelectString}"
304
369
  # @SEE_ALSO
305
370
  # array_join, array_describe, array_from_describe, string_pick_to_array
306
- function string_split_to_array() {
371
+ string_split_to_array() {
307
372
  local tokenString="$1"
308
373
  local newArrayVarName="$2"
309
374
  local string="${3-$(cat)}"
@@ -341,7 +406,7 @@ function string_split_to_array() {
341
406
  # string_pick_to_array '{age:' '}' newArray "$str"
342
407
  # @SEE_ALSO
343
408
  # array_join, array_describe, array_from_describe, string_split_to_array
344
- function string_pick_to_array() {
409
+ string_pick_to_array() {
345
410
  local startTokenString="$1"
346
411
  local endTokenString="$2"
347
412
  local newArrayVarName="$3"
package/src/tool.sh CHANGED
@@ -15,7 +15,7 @@ source src/out.sh
15
15
  # stop_if_failed "ERROR: can't delete the directory '${destProjectPath}' !"
16
16
  # @SEE_ALSO
17
17
  # confirm_to_continue
18
- function stop_if_failed() {
18
+ stop_if_failed() {
19
19
  if [[ $? -ne 0 ]]; then
20
20
  print_error "$*"
21
21
  exit 1
@@ -34,19 +34,25 @@ function stop_if_failed() {
34
34
  # confirm_to_continue a b
35
35
  # @SEE_ALSO
36
36
  # print_args, stop_if_failed
37
- function confirm_to_continue() {
37
+ confirm_to_continue() {
38
38
  local response
39
39
  print_args "$@"
40
- if ! [ "${modeQuiet}" == true ]; then
41
- read -r -p "Continue ? [y/N] " response
42
40
 
43
- case "${response}" in
44
- [yY][eE][sS] | [yY])
45
- echo -e "Continue..."
46
- sleep 1s
41
+ if [[ "${modeQuiet:-false}" != true ]]; then
42
+ # Bash `read -p` and zsh `read "name?prompt"` are shell-specific.
43
+ # Match Bash read -p behavior by showing the prompt only for a TTY.
44
+ if [[ -t 0 ]]; then
45
+ printf '%s' 'Continue ? [y/N] ' >&2
46
+ fi
47
+ read -r response || response=""
48
+
49
+ case "$response" in
50
+ [yY] | [yY][eE][sS])
51
+ printf '%s\n' 'Continue...'
52
+ sleep 1
47
53
  ;;
48
54
  *)
49
- echo -e "Exiting..."
55
+ printf '%s\n' 'Exiting...'
50
56
  exit 1
51
57
  ;;
52
58
  esac
@@ -65,7 +71,7 @@ function confirm_to_continue() {
65
71
  # wait_for 'test -f /tmp/output.txt' 'file existed' 3
66
72
  # @SEE_ALSO
67
73
  # confirm_to_continue, stop_if_failed
68
- function wait_for() {
74
+ wait_for() {
69
75
  local predicate="${1}"
70
76
  local subject="${2-${predicate}}"
71
77
  local interval=${3-3}
@@ -91,6 +97,16 @@ function wait_for() {
91
97
  # record2
92
98
  # EOF
93
99
  # @SEE_ALSO
94
- function declare_heredoc() {
95
- eval "$1='$(cat)'"
100
+ declare_heredoc() {
101
+ local _bash_base_heredoc_target="${1-}"
102
+ local _bash_base_heredoc_content
103
+
104
+ # Only identifier names may appear as the left-hand side of an eval.
105
+ case "$_bash_base_heredoc_target" in
106
+ '' | [!a-zA-Z_]* | *[!a-zA-Z0-9_]*) return 2 ;;
107
+ esac
108
+
109
+ _bash_base_heredoc_content="$(cat)"
110
+ # Expand the content as a variable value, never parse it as shell code.
111
+ eval "${_bash_base_heredoc_target}=\"\$_bash_base_heredoc_content\""
96
112
  }