pcut: delimit output by input delimiters by default
[hband-tools.git] / bash / bash-utils
blobf52ff0ac3bac3bfdf21614aee6e7d0ebc7f1c33b
1 #!/bin/bash
3 pipe()
5 if [ "$1" = --help ]
6 then
7 echo "NAME"
8 echo " pipe - mimic pipe(2) syscall in shell"
9 echo "INVOCATION"
10 echo " pipe <READER_VAR> <WRITER_VAR>"
11 echo "DESCRIPTION"
12 echo " Create anonymous pipe."
13 echo " Reader end's file descriptor stored in variable name in the first argument."
14 echo " Writer end's file descriptor stored in variable name in the second argument."
15 echo "EXAMPLE"
16 echo " pipe read_fd write_fd"
17 echo " echo Lorem ipsum >&\$write_fd"
18 echo " read -u \$read_fd data"
19 echo " echo \$data # => Lorem ipsum"
20 return
23 local pid1 pid2 myin myout lnk0 lnk1
24 [ "$1" = rdr ] || local rdr
25 [ "$2" = wtr ] || local wtr
27 myin=`readlink /proc/self/fd/0`
28 myout=`readlink /proc/self/fd/1`
30 # calling sleep(1) via env to avoid bash's built-in sleep which may not know 'inf'.
31 env sleep inf | env sleep inf &
32 pid2=$!
33 pid1=$(jobs -p %+)
35 while true
37 lnk0=`readlink /proc/$pid2/fd/0`
38 lnk1=`readlink /proc/$pid1/fd/1`
39 if [ "$lnk0" != "$myin" -a "$lnk1" != "$myout" ]
40 then
41 break
43 done
45 exec {wtr}>/proc/$pid1/fd/1 {rdr}</proc/$pid2/fd/0
47 disown $pid2
48 kill -KILL $pid1 $pid2 # sometimes INT and TERM are blocked, but it's safe to kill such a no-op process
50 eval $1=$rdr
51 eval $2=$wtr
54 close()
56 if [ "$1" = --help ]
57 then
58 echo "NAME"
59 echo " close - mimic close(2) syscall in shell"
60 echo "INVOCATION"
61 echo " close <FD_1> [FD_2 [FD_3 [...]]]"
62 echo "DESCRIPTION"
63 echo " Close file descriptor(s) given in arguments."
64 echo "EXAMPLE"
65 echo " pipe read_fd write_fd"
66 echo " close \$write_fd"
67 return
70 local fd
71 for fd in "$@"
73 #eval "exec $fd>&-"
74 exec {fd}>&-
75 done
78 capture2()
80 if [ "$1" = --help ]
81 then
82 echo "NAME"
83 echo " capture2 - store a command's stdout and stderr in separated variables"
84 echo "INVOCATION"
85 echo " capture2 <COMMAND> [<ARGS>]"
86 echo "DESCRIPTION"
87 echo " Capture <COMMAND>'s stdout and stderr in separated shell variables"
88 echo " without running it twice."
89 echo " Store stdout and stderr data in script-global variables"
90 echo " called \$capture2_stdout and \$capture2_stderr respectively."
91 echo "EXAMPLE"
92 echo " capture2 ls -lRA /etc"
93 echo " echo output was "'\$capture2_stdout'""
94 echo " echo error was "'\$capture2_stderr'""
95 echo "RETURN VALUE"
96 echo " <COMMAND>'s exit status"
97 echo "VARIABLES"
98 echo " using the following script-global variables"
99 echo " - capture2_stdout"
100 echo " - capture2_stderr"
101 echo "CAVEAT"
102 echo " May not work on large output, depend on fifo buffer size."
103 echo "SEE ALSO"
104 echo " pipe(1bash) close(1bash)"
105 return
108 local x r w
109 pipe r w
110 capture2_stdout=$("$@" 2>&$w)
111 x=$?
112 close $w
113 capture2_stderr=$(cat <&$r)
114 close $r
115 return $x
118 is_digit()
120 [ "$1" = 0 -o "$1" = 1 -o "$1" = 2 -o "$1" = 3 -o "$1" = 4 -o "$1" = 5 -o "$1" = 6 -o "$1" = 7 -o "$1" = 8 -o "$1" = 9 ]
123 is_number()
125 [ "$1" -ge 0 -o "$1" -lt 0 ] 2>/dev/null
128 is_alpha()
130 expr "$1" : '[a-zA-Z]\+$' >/dev/null 2>&1
133 warnx()
135 true "warnx - mimic warnx(3) in shell"
136 echo "${0##*/}: $*" >&2
139 errx()
141 true "errx - mimic errx(3) in shell"
142 errno=$1
143 shift
144 warnx "$@"
145 exit $errno
148 bash_isset()
150 local sets=${2:-$-}
151 sets=${sets//[!$1]/}
152 echo ${sets:-_}
155 bash_defined()
157 [ "${!1+x}" = x ]
160 bash_trapbody()
162 local a
163 a=`trap -p "$1"`
164 if [ -z "$a" ]
165 then
166 echo -
167 return
169 a=${a:9}
170 a=${a:0:-${#1}-2}
171 echo "$a"
174 try()
176 declare -g TRYSTACK
177 local savesets=$-
178 TRYSTACK+=($(bash_isset e $savesets)$(bash_isset E $savesets)"$(bash_trapbody ERR)")
179 trap - ERR
181 while [ -n "$1" ]
183 case "$1" in
184 --except)
185 shift
186 trap "$1" ERR
189 echo "Unknown option: $1" >&2
190 set -o errexit
191 false
193 esac
194 shift
195 done
197 set -o errexit
198 set -o errtrace
201 untry()
203 local trydata
204 trydata=${TRYSTACK[-1]}
205 unset TRYSTACK[${#TRYSTACK[@]}-1]
206 [ "${trydata:0:1}" = _ ] && set +o errexit || set -o errexit
207 [ "${trydata:1:1}" = _ ] && set +o errtrace || set -o errtrace
208 trap "${trydata:2}" ERR
209 return 0
212 fixerrexit()
214 ( eval "expr '$-' : '.*e' >/dev/null && set -e; $*"; )
217 template()
219 cat "$@" |\
221 local rline tvar var replace sedexpr
222 while read -r
224 rline=$REPLY
225 sedexpr=""
226 { grep -Eo '%[a-zA-Z0-9\./_-]+%' <<<"$rline" || true; } |\
227 sort -u |\
229 while read tvar
231 # strip percent marks
232 var=${tvar:1:-1}
233 # resolve bash variable
234 replace=${!var}
235 # avoid backrefs
236 replace=${replace//\\/\\\\}
237 # avoid whole match backref
238 replace=${replace//&/\\&}
239 # compose sed expression like: s@%user\.name%@joe@g;s@%user\.email%@joe\@local@g;
240 sedexpr="${sedexpr}s@${tvar//\./\\.}@${replace//@/\@}@g;"
241 done
242 # action
243 sed -e "$sedexpr" <<<"$rline"
245 done
249 errorlevel()
251 return $1
254 bash_join()
256 local IFS=$1
257 shift
258 echo "$*"
261 in_list()
263 local needle=$1
264 local hay
265 shift
266 for hay in "$@"
268 if [ ".$hay" = ".$needle" ]
269 then
270 return 0
272 done
273 return 1
276 array_shift()
278 if [ "$1" = --help ]
279 then
280 echo "NAME"
281 echo " array_shift - print and remove the first element from a bash indexed array"
282 echo "INVOCATION"
283 echo " array_shift <VARNAME>"
284 echo "EXAMPLE"
285 echo " shoplist=(bread milk sausage)"
286 echo " first_element=\$(array_shift shoplist)"
287 echo " next_element=\$(array_shift shoplist)"
288 return
291 local array_name=$1
292 local aux
293 eval "aux=\${$array_name[0]}"
294 unset $array_name[0]
295 eval "$array_name=(\"\${$array_name[@]}\")"
296 echo "$aux"
299 array_reverse()
301 if [ "$1" = --help ]
302 then
303 echo "NAME"
304 echo " array_reverse - reverse the order of elements in a bash array in-place"
305 echo "INVOCATION"
306 echo " array_reverse <VARNAME>"
307 echo "RETURN VALUE"
308 echo " void"
309 return
312 local array_name=$1
313 local i ci len aux
314 eval "len=\${#$array_name[@]}"
315 for ((i=0; i<len/2; i++))
317 ci=$[len-i-1]
318 eval "aux=\${$array_name[$i]}"
319 eval "$array_name[$i]=\${$array_name[$ci]}"
320 eval "$array_name[$ci]=\$aux"
321 done
324 bash_scriptdir()
326 dirname "$(readlink -f "${BASH_SOURCE[-1]}")"
329 ECHO()
331 printf %s "$*"
334 canonize_semver()
336 # return the canonical form of a version string
337 local ver=$1
338 # trailing ".0" is considered meaningless by SemVer
339 while [[ $ver =~ ^(.+)\.0+$ ]]
341 ver=$${BASH_REMATCH[1]}
342 done
343 echo "$ver"
346 version_compare()
348 # take 2 version numbers (as strings) in arguments, say A and B.
349 # return
350 # 0 when A equals to B,
351 # 1 when A is greater than B, or
352 # 255 (ie. -1) when A is less than B.
353 local ver_a=$1
354 local ver_b=$2
355 ver_a=`canonize_semver "$ver_a"`
356 ver_b=`canonize_semver "$ver_b"`
357 if [ "$ver_a" = "$ver_b" ]
358 then
359 return 0
361 # feed the 2 version numbers, 1 per line, to sort, then take the first line, which will be the lesser one.
362 local lesser_version=`echo "$ver_a"$'\n'"$ver_b" | sort --version-sort | sed -ne 1p`
363 if [ "$ver_a" = "$lesser_version" ]
364 then
365 return 255
366 else
367 return 1
371 version_is_lesser()
373 version_compare "$1" "$2"
374 if [ $? = 255 ]
375 then
376 return 0
377 else
378 return 1
382 version_is_greater()
384 version_compare "$1" "$2"
385 if [ $? = 1 ]
386 then
387 return 0
388 else
389 return 1
393 version_is_greater_or_equal()
395 ! version_is_lesser "$@"
398 version_is_lesser_or_equal()
400 ! version_is_greater "$@"