How to check if a given argument is a condition?

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

Answers (1)

KamilCuk
KamilCuk

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

Related Questions