- Why
- Features
- Preview
- Templating
- Usage
- Parsing Arguments
- Shell Configurations
- Trap Error and Exit
- Display Loader
- Call Loader
- Scrip Library Integration
- Conclusion
Why
I’ve been recently doing some old shell scripts to quickly automatize and distribute actions that were made manually, I’ll write a post on it later. For now I’d like to share with you how I’ve been coding a 100% shell loader library to use it in my scripts.
I’ve been looking on the net for existing library, I’ve found some interesting Github projects but it doesn’t appear to be “library” ready. Furthermore, it was mostly old basic ASCII templates and Iย wanted to give a try to modern loaders and spinners on shell.
So I ended create shloader
.
Features
shloader has nice features such as :
๐ emoji support
๐ช loader support
๐ dynamic message on load step
โน๏ธ message on step ending
๐จ multiple loading templates
๐ light and easy to use on existing scripts
Preview
Templating
First of all, we need to create templates, one for each loaders that will be displayed. I’ve decided to use shell array
so we can work on iteration later on customs functions.
So I ended with something like :
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
| # EMOJIS
emoji_hour=( 0.08 '๐' '๐' '๐' '๐' '๐' '๐' '๐' '๐' '๐' '๐' '๐' '๐')
emoji_face=( 0.08 '๐' '๐' '๐' '๐' '๐' '๐จ' '๐ก')
emoji_earth=( 0.1 ๐ ๐ ๐ )
emoji_moon=( 0.08 ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ )
emoji_orange_pulse=( 0.1 ๐ธ ๐ถ ๐ ๐ ๐ถ )
emoji_blue_pulse=( 0.1 ๐น ๐ท ๐ต ๐ต ๐ท )
emoji_blink=( 0.06 ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ ๐ )
emoji_camera=( 0.05 ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ท ๐ธ ๐ท ๐ธ )
emoji_sick=( 0.2 ๐คข ๐คข ๐คฎ )
emoji_monkey=( 0.2 ๐ ๐ ๐ ๐ )
emoji_bomb=( 0.2 '๐ฃ ' ' ๐ฃ ' ' ๐ฃ ' ' ๐ฃ' ' ๐ฃ' ' ๐ฃ' ' ๐ฃ' ' ๐ฃ' ' ๐ฅ' ' ' ' ' )
# ASCII
ball=( 0.2 '(โ)' '(โฌ)')
arrow=( 0.06 'โ' 'โ' 'โ' 'โ' 'โ' 'โ' 'โ' 'โ')
cym=( 0.1 'โ' 'โ' 'โ' 'โ')
x_plus=( 0.08 'ร' '+')
line=( 0.08 'โฐ' 'โฑ' 'โณ' 'โท' 'โถ' 'โด')
ball_wave=( 0.1 '๐๐๐' '๐๐โ' '๐โยฐ' 'โยฐโ' 'ยฐโ๐' 'โ๐๐')
old=( 0.07 'โ' "\\" '|' '/' )
dots=( 0.04 'โฃพ' 'โฃฝ' 'โฃป' 'โขฟ' 'โกฟ' 'โฃ' 'โฃฏ' 'โฃท' )
dots2=( 0.04 'โ ' 'โ ' 'โ น' 'โ ธ' 'โ ผ' 'โ ด' 'โ ฆ' 'โ ง' 'โ ' 'โ ' )
dots3=( 0.04 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ฆ' 'โ ด' 'โ ฒ' 'โ ณ' 'โ ' )
dots4=( 0.04 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ธ' 'โ ฐ' 'โ ' 'โ ฐ' 'โ ธ' 'โ ' 'โ ' 'โ ' 'โ ' )
dots5=( 0.04 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ฒ' 'โ ด' 'โ ฆ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' )
dots6=( 0.04 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ฒ' 'โ ด' 'โ ค' 'โ ' 'โ ' 'โ ค' 'โ ด' 'โ ฒ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' )
dots7=( 0.04 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ฆ' 'โ ค' 'โ ' 'โ ' 'โ ค' 'โ ฆ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' )
dots8=( 0.04 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ฒ' 'โ ด' 'โ ค' 'โ ' 'โ ' 'โ ค' 'โ ' 'โ ' 'โ ค' 'โ ฆ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' 'โ ' )
dots9=( 0.04 'โขน' 'โขบ' 'โขผ' 'โฃธ' 'โฃ' 'โกง' 'โก' 'โก' )
dots10=( 0.04 'โข' 'โข' 'โข' 'โก' 'โก' 'โก' 'โก ' )
dots11=( 0.04 'โ ' 'โ ' 'โ ' 'โก' 'โข' 'โ ' 'โ ' 'โ ' )
|
Array composition is divided in two parts, the first one is a time
interval in seconds and the second one is the loader frame transition.
Usage
I wanted library to be used as commands can be, by passing argument through options.
First, let’s work on default usage
function to display to user.
1
2
3
4
5
6
7
8
9
10
| usage() {
cat <<EOF
Available options:
-h, --help <OPTIONAL> Print this help and exit
-l, --loader <OPTIONAL> Chose loader to display
-m, --message <OPTIONAL> Text to display while loading
-e, --ending <OPTIONAL> Text to display when finishing
EOF
exit 0
}
|
I wanted to define argument options as bellow :
Display help
Parameter | Type | Description |
---|
-h --help | none | Optional. Show help usage |
Chose loader
1
2
| # e.g
shloader -l arrow
|
Parameter | Type | Description |
---|
-l --loader | string | Optional. Chose loader template |
Display info on loading
1
2
| # e.g
shloader -m "my loading message"
|
Parameter | Type | Description |
---|
-m --message | string | Optional. Show a text message while displaying loader |
Display info on ending
1
2
| # e.g
shloader -e "\u2728 all done"
|
Parameter | Type | Description |
---|
-e --end | string | Optional. Show an end text message when loader ends |
Parsing Arguments
In order to work on user input, it is necessary to read option content so we can modular library execution.
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
| shloader() {
loader=''
message=''
ending=''
while :; do
case "${1-}" in
-h | --help) usage;;
-l | --loader)
loader="${2-}"
shift
;;
-m | --message)
message="${2-}"
shift
;;
-e | --ending)
ending="${2-}"
shift
;;
-?*) die "Unknown option: $1" ;;
*) break ;;
esac
shift
done
[โฆ]
}
|
You might notice I’ve placed it directly in the main function
so it will be the first element to be run.
If user don’t specify loader, we should display one as default, let’s say dots
one.
1
2
3
4
5
6
7
8
9
10
| # shloader func
[โฆ]
if [[ -z "${loader}" ]] ; then
loader=dots[@]
else
loader=$loader[@]
fi
[โฆ]
|
One last thing here, I use a little custom die
function to exit properly if option is unknown.
1
2
3
4
| die() {
local code=${2-1}
exit "$code"
}
|
Shell Configurations
We will make some quick configuration on the top header script under shebang
.
1
2
3
4
5
6
| #!/bin/bash
# https://github.com/lebaronlebaron.shloader
# me@lebaron.sh
set -Eeuo pipefail
trap end_shloader SIGINT SIGTERM ERR EXIT RETURN
tput civis
|
First, set -Eeuo pipefail
so we can exit execution if one of the commands in the pipe fails.
Then, trap end_shloader SIGINT SIGTERM ERR EXIT RETURN
so we can call a custom function to clean our execution on exiting, stopping, errorsโฆ
Finaly, tput civis
is used to hide cursor.
Trap Error and Exit
Trap will call a custom function, but how does it work ?
1
2
3
4
5
6
7
| end_shloader() {
kill "${shloader_pid}" &>/dev/null
tput cnorm
if [[ "${ending}" ]]; then
printf "\r${ending}"; echo
fi
}
|
Right here I ensure first to kill loader’s PID
and restore cursor thanks to tput cnorm
.
Display Loader
Now we can interact with shell output to generate loading animations.
Let’s create a new function play_shloader()
:
1
2
3
4
5
6
7
8
| play_shloader() {
while true ; do
for frame in "${loader[@]}" ; do
printf "\r%s" "${frame} ${message}"
sleep "${speed}"
done
done
}
|
Here we assume we have loader
array we can display during time duration defined in speed
variable (we will see it after).
Call Loader
What about now ? We have all bricks to make great loaders, let’s put them in work together !
1
2
3
4
5
6
7
8
9
10
11
12
| # shloader function
[โฆ]
loader=( ${!loader} )
speed="${loader[0]}"
unset "loader[0]"
tput civis
play_shloader &
shloader_pid="${!}"
[โฆ]
|
What is done here ?
We first print loader
array content we save in loader
variable. We know first element in array is time duration, so we save it in speed
variable.
Now we have split array, we can remove time duration as it is now under speed
variable.
We then hide cursor, call our play_shloader
function and save PID
.
You can find the Full Library Code
Script Library Integration
Nothing hard here !
1
2
3
4
5
6
7
8
| source ./lib/shloader.sh
shloader -l emoji_hour -m "Testing" -e "โจ All good !"
sleep 2 # remove it in your code
# โฆ your logic goes here
end_shloader
|
With more details :
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
| !/bin/bash
# if you want to try just add this block code in your code
source ./lib/shloader.sh
# you can chose (see more in lib/loader.sh):
# ball, arrow, cym, x_plus, line, ball_wave, npm and old.
# you can specify message to display during loading
# and message to display after your code finished
# eg with npm style
# notice end message -e use unicode emoji to display
# this is for better terminal support
# \u2728 == โจ but you can use emoji if your settings support it !
shloader -l emoji_face -m "Testing" -e "\u2728 All good !"
sleep 2 # remove it in your code
# โฆ your logic goes here
# if you want to hide some output from loader
# don't forget to redirect your STD*
#
# eg :
# STDOUT
# my_cmd 1> /dev/null
# STDERR
# my_cmd 2> /dev/null
# BOTH
# my_cmd &> /dev/null
# stop loader
end_shloader
|
Conclusion
As all things uselessโฆ it may become mandatory ๐.