Correct way to check for a command line flag in bash
In the middle of a script, I want to check if a given flag was passed on the command line. The following does what I want but seems ugly:
if echo $* | grep -e "--flag" -q
then
echo ">>>> Running with flag"
else
echo ">>>> Running without flag"
fi
Is there a better way?
Note: I explicitly don't want to list all the flags in a switch/getopt. (In this case any such things would become half or more of the full script. Also the bodies of the if just set a set of vars)
Solution 1:
An alternative to what you're doing:
if [[ $* == *--flag* ]]
See also BashFAQ/035.
Note: This will also match
--flags-off
since it's a simple substring check.
Solution 2:
I typically see this done with a case statement. Here's an excerpt from the git-repack script:
while test $# != 0
do
case "$1" in
-n) no_update_info=t ;;
-a) all_into_one=t ;;
-A) all_into_one=t
unpack_unreachable=--unpack-unreachable ;;
-d) remove_redundant=t ;;
-q) GIT_QUIET=t ;;
-f) no_reuse=--no-reuse-object ;;
-l) local=--local ;;
--max-pack-size|--window|--window-memory|--depth)
extra="$extra $1=$2"; shift ;;
--) shift; break;;
*) usage ;;
esac
shift
done
Note that this allows you to check for both short and long flags. Other options are built up using the extra
variable in this case.
Solution 3:
you can take the straight-forward approach, and iterate over the arguments to test each of them for equality with a given parameter (e.g. -t
, --therizinosaurus
).
put it into a function:
has_param() {
local term="$1"
shift
for arg; do
if [[ $arg == "$term" ]]; then
return 0
fi
done
return 1
}
… and use it as a predicate in test expressions:
if has_param '-t' "$@"; then
echo "yay!"
fi
if ! has_param '-t' "$1" "$2" "$wat"; then
echo "nay..."
fi
if you want to reject empty arguments, add an exit point at the top of the loop body:
for arg; do
if [[ -z "$arg" ]]; then
return 2
fi
# ...
this is very readable, and will not give you false positives, like pattern matching or regex matching will.
it will also allow placing flags at arbitrary positions, for example, you can put -h
at the end of the command line (not going into whether it's good or bad).
but, the more i thought about it, the more something bothered me.
with a function, you can take any implementation (e.g. getopts
), and reuse it. encapsulation rulez!
but even with commands, this strength can become a flaw. if you'll be using it again and again, you'll be parsing all the arguments each time.
my tendency is to favor reuse, but i have to be aware of the implications. the opposed approach would be to parse these arguments once at the script top, as you dreaded, and avoid the repeated parsing.
you can still encapsulate that switch case, which can be as big as you decide (you don't have to list all the options).