BASH recursion examples - part 2
Created:30 Jan 2017 17:08:29 , in Host development
Asking for permission to carry on
Here is the first example of recursive function.
Imagine your script needs to ask for permission to proceed to the next stage of some automation process. Perhaps, that next stage consists of deletion of some important directories and files. You need to get clear yes (y), no (n) or abort (q) from the user before the scripts moves on.
Below is a function that asks for permission to continue. It reads user input and unless it gets y, n or q ( termination condition ) it calls itself again. Entering y as an answer to a question the function asks will set $? to 0, the other two valid answers will set it to 1 (More elaborate code could also distinguish between n and q).
can_begin() {
recursive
local answer=${answer:-''}
local regex='^(y|n|q)$'
echo "Do you want to begin now ? [y/n/q]"
read -r answer
[[ $answer =~ $regex ]] && {
[[ $answer == 'y' ]] && {
return
} || {
return 1
}
}
can_begin
}
Run it:
if can_begin; then
echo 'Beginning ...'
else
echo 'Aborting ...'
fi
Installing dependencies
A recursive function that installs programs (unless they are on the system already) with names stored in array called 'dependencies'. Internally, it uses apt-get utility to that ( necessary privileged access ).
dependencies=( rsync unzip gpg )
install_dependencies(){
recursive
local -i i="${i:-${#dependencies[@]}}"
(( i <= 0 )) && {
return
}
[[ -z $( which "${dependencies[$i-1]}" ) ]] && {
echo "${dependencies[$i-1]} needs to be installed. Installing ..."
apt-get install -y "${dependencies[$i-1]}"
} || {
echo "${dependencies[$i-1]} installed already."
}
((i--))
install_dependencies
}
Call it:
install_dependencies
Extending directory name
Suppose, you need a directory that is guaranteed not to exist before you create it.
The function below, called extend_dir_name, extends base directory name ( passed as the first argument ) by a word ( passed as the second argument ) recursively. It returns a new directory name, which is a combination of first and second argument joined with forward slash, only if that combination is not an existent directory name on the system. Otherwise it keeps on requesting for a new word.
extend_dir_name(){
ext="$2"
base="$1"
regex='/$'
[[ ! -d "$base" ]] && {
echo "$base must be an existing directory."
exit 1
}
[[ ! $base =~ $regex ]] && {
base="$base/"
}
[[ -z "$ext" ]] && {
echo "Type name of directory extension (single word):"
read -r ext
}
expanded="${base}${ext}"
[[ ! -d "$expanded" ]] && {
# return the new name
echo "$expanded"
return
# try again
} || {
ext=
extend_dir_name "$base" $ext
}
}
Run it:
$ mkdir $( extend_dir_name /tmp/ tmp_dir_extension )
Checking for existing system user
Next up is a function that asks for an existing system user name to be provided. At first it checks global variable USER ( it should be declared in the script ) for the name. If it finds nothing assigned it uses 'read' to get a name. In the next step the function tries to verify the name using 'id' utility. It calls itself again if if finds there is no user with the name on the system.
check_user_exists(){
recursive
local user=${user:-${USER}}
local exists=
[[ -z "$user" ]] && {
echo "Give existing user name:"
read -r user
}
exists=$(id -u "$user" > /dev/null 2>&1; echo $?)
[[ "$exists" == "0" ]] && {
USER="$user"
return
}
check_user_exists
}
Checking for existence of mysql database
Next function is somewhat similar to the previous one. When called with a name as its first argument it checks if a mysql database with the name exists on the system. When called with no arguments it ask for a name before carrying out the check. If it cannot find a database with the given name it will assign the name to DBNAME. Otherwise it will call itself.
When the function is called by the privileged user it looks up a name among all available database names, otherwise it sees only what is available to the current user.
For this function to work, mysql access (user and password) has to be configured in ~/.my.cnf (/root/.my.cnf for root user) file first.
check_mysqldb_exists() {
local mysqldb=${mysqldb:-$1}
local is_db=
[[ -z "${mysqldb}" ]] && {
echo "Specify database name:"
read -r mysqldb
}
is_db=$(mysql -s -N -e "SELECT schema_name FROM information_schema.schemata WHERE schema_name = '${mysqldb}'" information_schema)
[[ -z "$is_db" ]] && {
DBNAME="${mysqldb}"
echo "Setting DBNAME to ${mysqldb}"
return
}
echo "Database ${mysqldb} already exists."
mysqldb=
check_mysqldb_exists "$mysqldb"
}
Escaping globs and quotes
Recursion is often leveraged to write filters. Here is one of these. escape_globs_qotes_rec accepts a string as its input. It scans it in search of glob characters (glob characters in BASH are *, ?, [, ] ) and quotes ( ' and " ) which then it escapes using backslash (backslash also gets escaped in the process).
escape_globs_qotes_rec(){
local ESCD=${ESCD:-''}
local -i count=${#1}
local str="$1"
(( $count < 1 )) && {
echo "$ESCD"
return
}
read -r -n 1 -d '' <<< "$str"
case "$REPLY" in
'\'| "'" | '"' | '*' | '?' | '[' | ']')
ESCD+="\\${REPLY}"
;;
*)
ESCD+="$REPLY"
;;
esac
escape_globs_qotes_rec "${str:1:$count}"
}
Calling it might look like this:
escape_globs_qotes_rec "$(< file_with_data_to_escape )"
Unfortunately, possibly due to use of parameter expansions the function is rather slow. It is much slower than its equivalent that is based on while loop:
escape_globs_quotes(){
ESCD=''
while read -r -n 1 -d ''; do
case "$REPLY" in
'\'| "'" | '"' | '*' | '?' | '[' | ']')
ESCD+='\'"$REPLY"
;;
*)
ESCD+="$REPLY"
;;
esac
done <<< "$1"
echo "$ESCD"
}
You would call escape_globs_quotes the same way as you did for escape_globs_quotes_rec :
escape_globs_quotes "$(< file_with_data_to_escape )"
Back to good old loops ...
This post was updated on 06 Oct 2021 21:10:21
Tags: BASH , mysql , recursion
Author, Copyright and citation
Author
Author of the this article - Sylwester Wojnowski - is a sWWW web developer. He has been writing computer code for the websites and web applications since 1998.
Copyrights
©Copyright, 2024 Sylwester Wojnowski. This article may not be reproduced or published as a whole or in parts without permission from the author. If you share it, please give author credit and do not remove embedded links.
Computer code, if present in the article, is excluded from the above and licensed under GPLv3.
Citation
Cite this article as:
Wojnowski, Sylwester. "BASH recursion examples - part 2." From sWWW - Code For The Web . https://swww.com.pl//main/index/bash-recursion-examples-part-2
Add Comment