Reputation: 562
I am writing a simple bash script that plays an loading animation until the given condition is satisfied. I just want to check if the given condition is valid before proceeding with the animation.. Initially I had used the command_not_found_handle to do know whether the condition is valid or not, but I later realized that there are more use cases that I would need to take into consideration. Is there any better way to do that?
cmdname=`basename $0`
if [ $# -eq 0 ]; then
echo "The $cmdname command waits and renders a loading animation until the passed condition is satisfied."
echo
echo "Syntax: $cmdname condition [gap-between-each-state-in-ms] [states] "
fi
# Setting a handle to terminate the pro
command_not_found_handle() {
unset command_not_found_handle
echo "$cmdname: The condition passed was invalid." >&2
echo "Please re-check the passed condition..."
kill -PIPE $$
return 127
}
# Check if the passed condition is valid
if $1; then
# Since the passed condition is already true..exit with code 0
exit 0
fi
unset command_not_found_handle
states="| / - \\"
while :; do
for state in $states
do
sleep 0.1
if $1; then
exit 0
fi
echo -en "$state \b\b"
done
done
Output:
┌──(hello㉿DESKTOP-DB52R7M)-[/mnt/c/Users/admin/os]
└─$ ./loading "[ 1 == 1]"
./loading: line 19: [: missing `]'
./loading: line 32: [: missing `]'
./loading: line 32: [: missing `]'
./loading: line 32: [: missing `]'
./loading: line 32: [: missing `]'
./loading: line 32: [: missing `]'
[...]
Upvotes: 0
Views: 72
Reputation: 140990
How to check if a given argument is a condition?
I say - don't! A "condition" is a logical interpretation of the meaning of program execution and any "argument" may be a program to be executed and be a condition - even an invalid one. I propose to invert your script logic and rather continue until the condition is valid instead - that way you'll handle all erroneous cases and only keep the good ones.
I would:
...
echo "
Syntax: $cmdname [options] [--] COMMAND [ARGS...]
Stop printing a spinning thingi until COMMAND is exiting with zero exit status.
Options:
-c Remove all states
-s ARG Add new state
"
...
states=("|" "/" "-" "\\") # prefer bash arrays in bash
... Parse arguments with getopts
while :; do
for state in "${states[@]}"; do
if ! sh -c "$@"; then
# ^^^^ - pass a __properly quoted__ command
# ^^^^^ - I would force a subshell with a clear environment
# ^ - invert the logic
break
fi
printf "%s \b\b" "$state"
sleep 0.1
done
done
That way you could yourscript '[ 1 == 1 ]'
but also yourscript [ 1 == 1 ]
. But an invalid "condition" would exit with an error, like cmdname sh -c '[ 1 == 1]'
. For further information research word splitting, bash arrays and how quoting works in shell.
And see also similar commands, like how watch
handles shell scripts. watch
has -x --exec
flag, that instead of doing sh -c "$@"
does more like a "$@"
or better sh -c '"$@"' _ "$@"
- which allows to pass properly quoted arguments straight to be executed, like watch -x sh -c '[ "$(wc -l "$1")" -eq 1 ]' -- "filename with spaces inside"
. You could implement a similar flag in your script.
Upvotes: 1