-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbashlib.sh
More file actions
executable file
·390 lines (348 loc) · 10 KB
/
bashlib.sh
File metadata and controls
executable file
·390 lines (348 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
#!/usr/bin/env bash
#
# Bash library functions
#
# Copyright (C) 2018 Michael Davies <michael@the-davies.net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Or try here: http://www.fsf.org/copyleft/gpl.html
#
# Turn on debugging if requested
DEBUG="${DEBUG:-0}"
[[ $DEBUG -eq 1 ]] && set -xev
# Control codes to change colour on the terminal
FG_CLEAR="\033[0m"
FG_BLACK="\033[0;30m"
FG_RED="\033[0;31m"
FG_GREEN="\033[0;32m"
FG_YELLOW="\033[0;33m"
FG_BLUE="\033[0;34m"
FG_MAGNETA="\033[0;35m"
FG_CYAN="\033[0;36m"
FG_WHITE="\033[0;37m"
colourprintchar () {
LETTER_COLOUR=$FG_WHITE
UPPER_COLOUR=$FG_YELLOW
NUMBER_COLOUR=$FG_GREEN
SYMBOL_COLOUR=$FG_RED
case "$1" in
[a-z]) echo -ne "${LETTER_COLOUR}${1}${FG_CLEAR}";;
[A-Z]) echo -ne "${UPPER_COLOUR}${1}${FG_CLEAR}";;
[0-9]) echo -ne "${NUMBER_COLOUR}${1}${FG_CLEAR}";;
*) echo -ne "${SYMBOL_COLOUR}${1}${FG_CLEAR}";;
esac
}
colourprint () {
str=$1
for ((i=0; i<${#str}; i++)); do
colourprintchar ${str:$i:1}
done
echo ""
}
# Check to see if this library is being sourced
IS_SOURCED=1
SCRIPTNAME=unknown
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
SCRIPTNAME=$(basename $0)
IS_SOURCED=0
fi
# Check to see if this is an interactive shell
IS_INTERACTIVE=0
if [[ $- == *i* ]]; then
IS_INTERACTIVE=1
fi
# See if a certain command is available
is_available() {
command -v "$1" &> /dev/null
}
# Run command, including it's parms, if available
run_if_avail() {
if is_available "$1" ; then
$@
fi
}
# Guess which family of operating system we're running on
# Possible return values are "Red Hat" or "Ubuntu"
guess_os ()
{
if [ -f /etc/os-release ]; then
OS=$(grep ^NAME= /etc/os-release | cut -f2 -d=)
fi
if [ "$OS" = "\"Fedora Linux\"" ] || \
[ "$OS" = "\"Red Hat Enterprise Linux\"" ]; then
echo "Red Hat"
elif [ "$OS" = "Ubuntu" ]; then
echo "Ubuntu"
else
echo "Unknown"
fi
}
# Find a operating system package for $1
find_pkg() {
# Let's see if we can find out what package we need
OS=$(guess_os)
if [ "$OS" = "Red Hat" ]; then
# Can't use ensure_cmd because of circular dependencies
if is_available "dnf" ; then
DNF_OUTPUT=$(dnf whatprovides "$1" 2> /dev/null)
DNF_RET_CODE=$?
if [ $DNF_RET_CODE -eq 0 ]; then
printf "To resolve this you need to install one of these:\n"
printf "${DNF_OUTPUT}\n"
fi
else
echo "You need to install \"dnf\" first"
fi
if [ $DNF_RET_CODE -ne 0 ]; then
echo "Could not find an operating system package for \"$1\""
fi
# TODO: Add support for debian/ubuntu
#elif [ "$OS" = "Ubuntu" ]; then
fi
}
# Ensure a certain command is available, exit if it doesn't
# $1: the command to test for
ensure_cmd() {
if ! command -v "$1" &> /dev/null; then
echo "*** Required command \"$1\" doesn't exist"
find_pkg $1
[[ $IS_SOURCED -ne 1 ]] && exit 3
fi
}
ensure_cmd basename
ensure_cmd id
ensure_cmd stat
ensure_cmd zenity
# See if a process is running
check_process_running() {
$(ps aux | grep "$1" | grep -v grep >& /dev/null);
}
# Get a non-empty string and store it in the provided variable
# $1: variable to update
# $2: prompt string
get_non_empty_str ()
{
local EXIT_LOOP=0
while [[ ${EXIT_LOOP} -ne 1 ]];
do
read -p "${2}" -r
if [[ ! -z "${REPLY}" ]]; then
eval "${1}='"${REPLY}"'"
EXIT_LOOP=1
fi
done
}
# Ensure a certain directory exists, if it doesn't, create it if possible
# $1: the directory to test for/create
ensure_dir ()
{
if [ ! -d "${1}" ]; then
mkdir -p ${1} >& /dev/null
if [[ $? -ne 0 ]]; then
echo "*** Couldn't create directory ${1}"
exit 2
fi
fi
}
# Change to the specified directory if possible, exit if we can't
# $1: the directory to go to
change_dir ()
{
if [[ ! "z$(pwd)" == "z${1}" ]]; then
cd ${1}
if [[ $? -ne 0 ]]; then
echo "*** Couldn't change directory to ${1}"
exit 3
else
echo "--- Changing directory to $(pwd)"
fi
fi
}
# Check that there's enough disk space available on the local filesystem
# Exit if there isn't
# $1: the required amount of disk space
check_disk_space ()
{
local FREE_SPACE=$(($(stat -f --format="%a*%S" .)))
if [[ ${FREE_SPACE} -lt ${1} ]]; then
echo "*** Not enough disk space. ${FREESPACE} available, require ${1}."
echo "Exiting..."
exit 4
fi
}
# Check that this script is running as the specified user, exit if not
# $1: the user this should be running as
check_user ()
{
if [ "z$(id -u -n)" != "z${1}" ]; then
echo "${SCRIPT}: Please re-run this script as the '${1}' user"
exit 5
fi
}
# Prompt the user with a string, asking whether the script should continue
# Y means continue, N means exit
# $1: prompt string
confirm_continue ()
{
local EXIT_LOOP=0
while [[ ${EXIT_LOOP} -ne 1 ]]; do
read -p "${1}" -r
if [[ ${REPLY} =~ ^[Nn]$ ]]; then
echo "Exiting..."
exit 6
fi
if [[ ${REPLY} =~ ^[Yy]$ ]]; then
EXIT_LOOP=1
fi
done
}
# Allow the user to choose from a number of options
# $1: prompt string
# $2: array of choices
# $3: the variable to update with the selected choice
choose_option ()
{
local OLDIFS=$IFS
IFS=$'\n'
declare -a OPT=("${!2}")
local EXIT_LOOP=0
while [ $EXIT_LOOP -ne 1 ];
do
j=0
echo $1
for i in ${OPT[@]}
do
echo $j - $i
j=$[j+1]
done
read -p "Which option would you like to choose? " -r
if [ ${REPLY} == "q" ]; then
EXIT_LOOP=1
else
if [ ${REPLY} -ge 0 ] && [ ${REPLY} -lt $j ] || \
[ ${REPLY} == "q" ]; then
EXIT_LOOP=1
else
echo "\"${REPLY}\" is not a valid option, please choose again"
fi
fi
done
IFS=$OLDIFS
if [ ${REPLY} == "q" ]; then
eval "${3}='"${REPLY}"'"
else
eval "${3}='"${OPT[$REPLY]}"'"
fi
}
# Print the character "$1" the number of times given by "$2"
print_char ()
{
# $3 is a secret param, provided to help print_line :)
printf "%0$2s$3" | tr " " "$1"
}
# Print the character "$1" the number of times given by "$2" with newline
print_line ()
{
print_char $1 $2 $'\n'
}
# Display the message $1 in a box made up of $2 characters,
# with $2 being optional
# Note that you need to handle \n's correctly, i.e.
# print_message_box $'This is a \ntest message\nsplit over several lines' 'X'
# print_message_box $'This is a \ntest message\nsplit over several lines'
print_message_box ()
{
local CHAR ARR MAXLEN BOXLEN
# If no box char specified...
CHAR=$2
[[ "z${CHAR}" == "z" ]] && CHAR="#"
readarray ARR <<< $1
# Find longest line length, and remove pesky newline characters
MAXLEN=0
for IDX in "${!ARR[@]}"; do
ARR[IDX]=$(tr -d $'\n' <<< ${ARR[IDX]})
[[ ${#ARR[IDX]} -gt $MAXLEN ]] && MAXLEN=${#ARR[IDX]}
done
BOXLEN=$((MAXLEN+4)) # $CHAR and a space either side
print_line ${CHAR} ${BOXLEN}
for ELEM in "${ARR[@]}"; do
printf "${CHAR} %-${MAXLEN}s ${CHAR}\n" "${ELEM}"
done
print_line ${CHAR} ${BOXLEN}
}
# Busy-wait for $1 minutes, using $2 as a title (optional)
# If $2 isn't supplied, "Time Remaining:" is used
# This relies on having a smart tty device that can handle line redraws
timebar() {
TOTSECS=$(( $1 * 60 ))
MINS=$(($1-1))
LEN=30
TITLE=${2:-"Time Remaining:"}
for m in $(seq $MINS -1 0) ; do
for s in $(seq 59 -1 0) ; do
SECS_REMAINING=$(( $m*60 + $s ))
PERCENT=$(bc <<< "scale=0; ($TOTSECS - $SECS_REMAINING) * 100 / $TOTSECS")
NUMHASHES=$( bc <<< "scale=0; (($LEN * $PERCENT) / 100)" )
NUMDOTS=$( bc <<< "scale=0; $LEN - $NUMHASHES" )
HASHES=""
if [ $NUMHASHES -ne 0 ]; then
HASHES=$( printf "%-${NUMHASHES}s" "#" )
HASHES=${HASHES// /#}
fi
DOTS=""
if [ $NUMDOTS -ne 0 ]; then
DOTS=$( printf "%-${NUMDOTS}s" "." )
DOTS=${DOTS// /.}
fi
printf "\r%s %02d:%02d [ %s%s ] %01d%% " "$TITLE" $m $s "$HASHES" "$DOTS" $PERCENT
sleep 1s
done
done
echo ""
}
beep ()
{
echo -en "\007"
}
# Display a modal dialog box, blocking until the user acks
# $1 is the title
# $2 is the message to display
dialogbox ()
{
TITLE=${1:-"Title goes here"}
BODY=${2:-"Message goes here"}
zenity --info --text="$BODY" --title="$TITLE" --width=300 --height=200 >& /dev/null
}
# Re-run this script in the background in a seamless way
# Note that you lose stdin and stdout as a result
backgroundme ()
{
PASS="startinthebackground"
if [ "$SCB" != "$PASS" ]; then
export SCB="$PASS"
nohup "$0" "$@" </dev/null >/dev/null 2>&1 &
exit
fi
}
# Print the time elapsed for the supplied commands presented as arguments
timeelapsed()
{
STARTTS=$(date "+%s")
bash -c "$@"
ENDTS=$(date "+%s")
DIFF=$(( $ENDTS - $STARTTS ))
echo -n "Time Taken: $(( $DIFF /3600 )) hours, "
echo "$(( $DIFF /60 )) minutes, and $(( $DIFF % 60 )) seconds"
}