Bash case statement - bash

I'm trying to learn case as I was to write a fully functional script.
I'm starting off with the below
#!/bin/sh
case $# in
-h|--help)
echo "You have selected Help"
;;
-B|-b)
echo "You have selected B"
;;
-C|-c)
echo "You have selected C"
;;
*)
echo "Valid Choices are A,B,C"
exit 1
;;
esac
I want to use two of these options:
./getopts.sh -h -c
But i get this result
Valid Choices are A,B,C
Please can you help out and let me know what I'm doing wrong?
I want to build a script that will do something if you enter one option but do multiple things if you enter multiple.
Also how would i parse $1 to this script as surley which ever option i enter first (-h) will be $1 ??
Thanks!

Try this
#!/bin/sh
usage() {
echo `basename $0`: ERROR: $* 1>&2
echo usage: `basename $0` '[-a] [-b] [-c]
[file ...]' 1>&2
exit 1
}
while :
do
case "$1" in
-a|-A) echo you picked A;;
-b|-B) echo you picked B;;
-c|-C) echo you picked C;;
-*) usage "bad argument $1";;
*) break;;
esac
shift
done

Using getopt or getopts is the better solution. But to answer your immediate question, $# is all of your arguments, so -h -c, which doesn't match any of the single-argument patterns in your case statement. You would still need to iterate over your arguments like so
for arg in "$#"; do
case $arg in
....
esac
done

to parse the positional arguments like ... $1 , just use $1 in the case stmt and then at the end ... use shift to pust the 2nd arg to $1 and likewise .
also i would put the case stmt in a while loop or better a fxn so that i can run it twice for the two options or the number of options ..........
$# will let you know how many options/arguments were there .

Related

Bash getopts differentiate "-?" from invalid options

I want getopts to recognize when a user passes "-?" and "-h" and send to my help function.
I also don't want getopts to send the user to the help function if an invalid option is presented. I want a different message to display telling them it was an invalid option and send to stderr.
Here is my options section:
options(){
# grab options flags before entering application:
while getopts ":h?vu:d" opts; do
case "${opts}"
in
h\?) # Help
help_section
exit 0
;;
v) # Version
version
exit 0
;;
u) # units
other_units="$OPTARG"
;;
d) # Debug
debug=1
;;
\?) # Invalid options
echo "no here"
>&2 echo "[!] ERROR: Invalid Option: -$OPTARG"
exit 1
;;
esac
done
shift $((OPTIND -1))
# Run main function
main_run "$#"
}
Problem: getopts keeps sending the user to help when they put an invalid option in. I need to make sure the user recognizes they provided an invalid parameter; I do not want to send them to help.
Is there a way to implement this with getopts? Or build logic outside of getopts to capture and perform what I need?
Bash Command Line Input Example
The following example shows how to take various types of inputs and default to an invalid input option.
# -- Get input options (if any)
function get_user_input_options() {
if [[ $# -lt 1 ]];then
echo "no input, help infomration"
#show_help_info_function
exit
fi
while [[ $# > 0 ]] ;do
key="$1"
case ${key,,} in
-o|--opt)
echo " we a seting option=$2"
option=2
shift
;;
-\?|-h|--\?|--help)
echo "help information"
#show_help_info_function # a function that prints help and exits
exit;
;;
*)
echo "not understanding your input at all!"
exit;
;;
esac
shift
done
}
get_user_input_options "$#"
What is going on here?
We read in all of the inputs and based on the type we can shift to the next or shift twice. If you see --opt is used, it is expecting something after which would be set to a variable. More on point of your issue, we accept -? or --? and if that happens it will do what is within the section of help; if none of these inputs is processed, it will do whatever the default is in the section '*' which means any input. Note: this is just an example and in this case the help section causes the script to stop even if other inputs were provided, this may or may not be what you personally are looking to do.
Example output
$ ./args.sh -o something
we a seting option=something
$ ./args.sh -j somethingelse
not understading your input at all!
$ ./args.sh -?
help information
$ ./args.sh
no input, help infomration
$ ./args.sh -h
help information

Bash optarg fails to spot missing arguments

I am inexperienced with bash shell scripting, and have run into a problem with bash optarg
Here's a small script to reproduce the problem:
#!/bin/sh
while getopts ":a:b:" opt; do
case ${opt} in
a ) echo "a=$OPTARG"
;;
b ) echo "b=$OPTARG"
;;
\? ) echo "Invalid option: $OPTARG" 1>&2
;;
: ) echo "Invalid option: $OPTARG requires an argument" 1>&2
esac
done
When I try this:
./args.sh -a av -b bv
I get the expected result:
a=av
b=bv
But when I omit the argument for -a:
/args.sh -a -b bv
I get this unfortunate result:
a=-b
When I would expect an error to show that the value of -a is missing.
It seems to have taken the -b argument as the value for -a.
Have I done something wrong & how can I achieve the expected behaviour?
The only positive advice is how do you treat But when I omit the argument for '-a', you cannot just skip to the next subsequent option. By convention getopts a: means you are expecting to an provide an arg value for the flag defined.
So even for the omitting case, you need to define an empty string which means the value for the arg is not defined i.e.
-a '' -b bv
Or if you don't expect the -a to get any arg values, better change the option string to not receive any as :ab:.
Any other ways of working around by checking if the OPTARG for -a is does not contain - or other hacks are not advised as it does not comply with the getopts() work flow.
getopts doesn't support such detection. So there's no way to do that with getopts.
You can probably write a loop around the arguments instead. something like:
#!/bin/sh
check_option()
{
case $1 in
-*)
return 1
;;
esac
return 0
}
for opt in $#; do
case ${opt} in
-a) shift
if check_option $1; then
echo "arg for -a: $1"
shift
else
echo "Invalid option -a"
fi
;;
-b) shift
if check_option $1; then
echo "arg for -b: $1"
shift
else
echo "Invalid option -b"
fi
;;
esac
done

bash script case statement needs to detect specific arguments

I have to write this script where it will display each entry that is entered in on its own line and separate each line with "*****". I've already got that down for the most part but now I need it to detect when the word "TestError" and/or "now" is entered in as an argument. The way it is setup right now it will detect those words correctly if they are the first argument on the line, I'm just not sure how to set it up where it will detect the word regardless of which argument it is on the line. Also I need help with the *? case where I need it to say "Do not know what to do with " for every other argument that is not "TestError" or "now", at the moment it will do it for the first argument but not the rest.
Would it work the way it is right now? Or would I have to use only the *? and * cases and just put an if/then/else/fi statement in the *? case in order to find the "TestError" "now" and any other argument.
# template.sh
function usage
{
echo "usage: $0 arguments ..."
if [ $# -eq 1 ]
then echo "ERROR: $1"
fi
}
# Script starts after this line.
case $1 in
TestError)
usage $*
;;
now)
time=$(date +%X)
echo "It is now $time"
;;
*?)
echo "My Name"
date
echo
usage
printf "%s\n*****\n" "Do not know what to do with " "$#"
;;
*)
usage
;;
esac
You'll need to loop over the arguments, executing the case statement for each one.
for arg in "$#"; do
case $arg in
TestError)
usage $*
;;
now)
time=$(date +%X)
echo "It is now $time"
;;
*?)
echo "My Name"
date
echo
usage
printf "%s\n*****\n" "Do not know what to do with " "$#"
;;
*)
usage
;;
esac
done
* and *? will match the same strings. Did you mean to match the ? literally (*\?)?

Best way to parse arguments in bash script

So I've been reading around about getopts, getopt, etc. but I haven't found an exact solution to my problem.
The basic idea of the usage of my script is:
./program [-u] [-s] [-d] <TEXT>
Except TEXT is not required if -d is passed. Note that TEXT is usually a paragraph of text.
My main problem is that once getopts finishing parsing the flags, I have no way of knowing the position of the TEXT parameter. I could just assume that TEXT is the last argument, however, if a user messes up and does something like:
./program -u "sentence 1" "sentence 2"
then the program will not realize that the usage is incorrect.
The closest I've come is using getopt and IFS by doing
ARGS=$(getopt usd: $*)
IFS=' ' read -a array <<< "$ARGS"
The only problem is that TEXT might be a long paragraph of text and this method splits every word of text because of the spaces.
I'm thinking my best bet is to use a regular expression to ensure the usage is correctly formed and then extract the arguments with getopts, but it would be nice if there was a simpler solution
It's quite simple with getopts:
#!/bin/bash
u_set=0
s_set=0
d_set=0
while getopts usd OPT; do
case "$OPT" in
u) u_set=1;;
s) s_set=1;;
d) d_set=1;;
*) # getopts produces error
exit 1;;
esac
done
if ((!d_set && OPTIND>$#)); then
echo You must provide text or use -d >>/dev/stderr
exit 1
fi
# The easiest way to get rid of the processed options:
shift $((OPTIND-1))
# This will run all of the remaining arguments together with spaces between them:
TEXT="$*"
This is what I typically do:
local badflag=""
local aflag=""
local bflag=""
local cflag=""
local dflag=""
while [[ "$1" == -* ]]; do
case $1 in
-a)
aflag="-a"
;;
-b)
bflag="-b"
;;
-c)
cflag="-c"
;;
-d)
dflag="-d"
;;
*)
badflag=$1
;;
esac
shift
done
if [ "$badflag" != "" ]; do
echo "ERROR CONDITION"
fi
if [ "$1" == "" ] && [ "$dflag" == "" ]; do
echo "ERROR CONDITION"
fi
local remaining_text=$#

What is the best way to check the getopts status in bash?

I am using following script :
#!/bin/bash
#script to print quality of man
#unnikrishnan 24/Nov/2010
shopt -s -o nounset
declare -rx SCRIPT=${0##*/}
declare -r OPTSTRING="hm:q:"
declare SWITCH
declare MAN
declare QUALITY
if [ $# -eq 0 ];then
printf "%s -h for more information\n" "$SCRIPT"
exit 192
fi
while getopts "$OPTSTRING" SWITCH;do
case "$SWITCH" in
h) printf "%s\n" "Usage $SCRIPT -h -m MAN-NAME -q MAN-QUALITY"
exit 0
;;
m) MAN="$OPTARG"
;;
q) QUALITY="$OPTARG"
;;
\?) printf "%s\n" "Invalid option"
printf "%s\n" "$SWITCH"
exit 192
;;
*) printf "%s\n" "Invalid argument"
exit 192
;;
esac
done
printf "%s is a %s boy\n" "$MAN" "$QUALITY"
exit 0
In this if I am giving the junk option :
./getopts.sh adsas
./getopts.sh: line 32: MAN: unbound variable
you can see it fails. it seems while is not working. What is the best way to solve it.
The getopts builtin returns 1 ("false") when there are no option arguments.
So, your while never executes unless you have option arguments beginning with a -.
Note the last paragraph in the getopts section of bash(1):
getopts returns true if an option, specified or unspecified, is
found. It returns false if the end of options is encountered or
an error occurs.
If you absolutely require MAN, then i suggest you don't make it an option parameter, but a positional parameter. Options are supposed to be optional.
However, if you want to do it as an option, then do:
# initialise MAN to the empty string
MAN=
# loop as rewritten by DigitalRoss
while getopts "$OPTSTRING" SWITCH "$#"; do
case "$SWITCH" in
m) MAN="$OPTARG" ;;
esac
done
# check that you have a value for MAN
[[ -n "$MAN" ]] || { echo "You must supply a MAN's name with -m"; exit 1; }
Even better, print the usage message before exiting - pull it out into a function so you can share it with the -h option's case.
The "best" solution is subjective. One solution would be to give default values to those variables that can be set by options.

Resources