Bash Shell Specify valid options for getops - linux

This is the code I'm working with:
TYPE=""
FILE=""
while getopts "t:f:" opt; do
case $opt in
t) TYPE="$OPTARG"
;;
f) FILE="$OPTARG"
;;
esac
done
if [ -z "$TYPE" ]; then
echo "No -t. Bye."
exit 1 # error
else
if [ -n "$FILE" ]; then
echo "$TYPE and $FILE"
else
echo JUST $TYPE
fi
fi
Is it possible to specify valid options for $TYPE? For example valid type options are:
IMAGE, ZIP, DOC
If one of these types are specified as valid arguments then the script runs the existing line:
"echo "$TYPE and $FILE""
Otherwise it echos an error and quits. Is this possible to do?

If you need to filter out -t switch :
(...)
t)
case $OPTARG in
img|image|doc)
TYPE="$OPTARG"
;;
*)
echo >&2 "Unsupported type..."
exit 1
;;
esac
;;
(...)

Related

How to not allow the user to specify two options at once in getopts?

In getopts user can specify all the options that we introduce in the code. Give the following script.
while getopts d:s o
do case "$o" in
d) seplist="$OPTARG";;
s) paste=hpaste;;
[?]) print >&2 "Usage: $0 [-s] [-d seplist] file ..."
exit 1;;
esac
done
The user should not be allowed to specify both option -d and -s. ie.
When the user runs above script with both options -d and -s, he should receive an error cannot specify both -d and -s.
A naive implementation would be to maintain an $OPTION_COUNT :
OPTION_COUNT=0
while getopts d:s o
do case "$o" in
d) seplist="$OPTARG"; (( OPTION_COUNT ++ );;
s) paste=hpaste; (( OPTION_COUNT ++ );;
[?]) print >&2 "Usage: $0 [-s] [-d seplist] file ..."
exit 1;;
esac
done
if [ "$OPTION_COUNT" -gt 1 ]; then echo "too many options"; fi
You should check for particular options passed into the script. It will be much easier to maintain it.
#!/usr/bin/env bash
d_option=0
s_option=0
while getopts d:s o
do case "$o" in
d)
seplist="$OPTARG"
d_option=1
;;
s)
paste=hpaste
s_option=1
;;
[?]) print >&2 "Usage: $0 [-s] [-d seplist] file ..."
exit 1;;
esac
done
if [ "x$d_option" == "x1" ] && [ "x$s_option" == "x1" ]; then
echo "both options specified."
exit 1
fi
You should check for hints of other option.
while getopts d:s o
do case "$o" in
d) if [ -z "$paste" ]; then
seplist="$OPTARG"
else
print >&2 "Option -s is already specified"
exit 1
fi
;;
s) if [ -z "$seplist" ]; then
paste=hpaste
else
print >&2 "Option -d is already specified"
exit 1
fi
;;
[?]) print >&2 "Usage: $0 {-s | -d seplist} file ..."
exit 1;;
esac
done

Bash: using getops for this specific use

I would need my bash scrip to work either with:
No arguments
./script.sh
Path argument (classic argument, will saved as var to var=$1)
./script.sh /root/home/dir/
With switch -a (with it's own argument)
./script.sh -a picture.jpeg
Or both combined
./script.sh -a picture.jpeg /root/home/dir/
I have something like this:
while getopts ":a:" opt; do
case $opt in
a)
I_ARGUMENT=$OPTARG
echo "A ARGUMENT IS: $OPTARG"
;;
:)
echo "-a requires argument"
;;
esac
done
And then something like this for the path argument:
if [ -z "$1" ]
then
:
else
PATH="$1"
fi
Which obviously doesn't work well (at all) together. Could you help me to combine this two things? Thank you.
How about something like this
pathToHomeSlashDir=~/dir/ #default value will be home/dir/
if [ $# -gt 0 ]
then
while [ $# -gt 0 ]
do
case "$1" in
-a)
if [ $# -gt 1 ]
then
image="$2"
shift
else
echo "Please insert -a argument"
exit
fi
;;
*)
pathToHomeSlashDir="$1"
;;
esac
shift
done
fi
You'd just need to handle the variables then

Bash multiple combinations in getopts

I would like to make a script where you can give a couple of parameters with it:
while getopts ":a:b:c:" opt; do
case $opt in
a)
echo "-a was triggered
;;
b)
echo "-b was triggered
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
The code I have now works but the problem I have is that I want to give another function/echo if it combines.
Example:
When I do: .MyScript -ab than it should give another function that what is defined in "a" or in "b"
so a bit like:
ab) -> Script -a -b or Script -ab
echo "-ab was triggered"
What is the best solution to do this?
Any ideas, your free to post!
while getopts ":a:b:c:" opt; do
case $opt in
a)
echo "-a was triggered"
a_val=$OPTARG
;;
b)
echo "-b was triggered"
b_val=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
if [[ -n $a_val ]]; then do something with "$a_val"; fi
if [[ -n $b_val ]]; then do something with "$b_val"; fi
The only situation this may cause confusion is if the user passes -a "" -- one workaround is:
a=false
b=false
while getopts ":a:b:c:" opt; do
case $opt in
a) echo "-a was triggered"; a_val=$OPTARG; a=true ;;
b) echo "-b was triggered"; b_val=$OPTARG; b=true ;;
esac
done
if $a; then do something with "$a_val"; fi
if $b; then do something with "$b_val"; fi
Here, there are no brackets: if $a; then ... because I'm invoking the command "true" or "false" and acting on that exit status.
UPDATED
You can use the getopt(1) linux utility. This is not a bash internal function, but this can handle long arguments.
Try this:
declare -A OPTARG
GetOpt() {
local prog="${BASH_SOURCE[0]}"
[ $# -eq 0 ] && echo "$prog: Bad call of GetOpts">&2 && exit 1
local longopt="$1"
shift
local tmp
tmp=$(getopt -n"$prog" -a -l "$longopt" -- '' "$#") || exit
eval "tmp=($tmp)"
local i
for((i=0;i<${#tmp[*]};++i)){
key=${tmp[i]#--}
[ -z "$key" ] && break
if [[ "${tmp[i+1]}" =~ ^-- ]]; then OPTARG[$key]=1
else OPTARG[$key]="${tmp[++i]}"
fi
}
for((j=0;++i<${#tmp[*]};++j)){ OPTFILE[j]=${tmp[i]};}
}
GetOpt "a b ab file:" "$#"
[ "${OPTARG[a]}" -a "${OPTARG[b]}" ] && OPTARG[ab]=1 && unset OPTARG[a] OPTARG[b]
[ "${OPTARG[a]}" ] && echo Do a
[ "${OPTARG[b]}" ] && echo Do b
[ "${OPTARG[ab]}" ] && echo Do ab
The defined GetOpt function will place the parsed command line argument with the checked long options to the associative array called OPTARG. If there is an error it will fail informing about the problem.
With getopt's -a option you can use -ab or --ab format. Keep in mind that if You have del defined, then the -d option (if -d is not specified) will expand to --del.
If You specify an arg with additional option it can contain spaces. E.g. if -l file: defined in getopt then it can be used as ./test --file="q w". ${OPTARG[file]} will be q w.
The OPTFILE array contains the optional arguments (args given after --).

Shell getopts for grabbing arguments

I have a script which should be run as either one of these two:
script.sh -t TYPE
script.sh -t TYPE -f FILE
If it is run without a -t flag I want it to error and exit.
If it is run with a -t flag I want to grab the value and store it in a
variable called "$TYPE" and print "JUST $TYPE"
If it is run with a -f flag I want it grab the value and store it in a variable called
"$FILE" and print "$TYPE and $FILE"
From information and tutorials on both here and the internet generally this is the closest I can get. Can anyone help me put in the second conditional into this existing code?
while getopts ":t:" opt; do
case $opt in
a)
echo "JUST $OPTARG" >&2
;;
\?)
echo "Error - Invalid type argument" >&2
exit 1
;;
:)
echo "Error - No type argument" >&2
exit 1
;;
esac
done
I think you get confused how you should handle command line arguments.
The common way is that the processing of all arguments precedes the actual job of the program/script.
Further more (related to getopts) if an option is appended by a colon, that indicates that the option is expected to have an argument.
Your case statement looks overpopulated too. You don't need to test for a colon and a question mark. The whole testing can be put after the while loop
I would do it like this
#!/bin/bash
unset TYPE
unset FILE
#uncomment if you want getopts to be silent
#OPTERR=0
while getopts "t:f:" opt; do
case $opt in
t)
TYPE=$OPTARG
echo "JUST $OPTARG"
;;
f)
FILE=$OPTARG
;;
esac
done
if ! test "$TYPE" ; then
echo "-t is obligatory"
exit 1
fi
if test "$TYPE" && test "$FILE" ; then
echo "$TYPE and $FILE"
fi
Have a look at this:
TYPE=""
FILE=""
while getopts "t:f:" opt; do
case $opt in
t) TYPE="$OPTARG"
;;
f) FILE="$OPTARG"
;;
esac
done
if [ -z "$TYPE" ]; then
echo "No -t. Bye."
exit 1 # error
else
if [ -n "$FILE" ]; then
echo "$TYPE and $FILE"
else
echo JUST $TYPE
fi
fi

Implementing bash shell getops code

I got help to create two seperate pieces of code:
FIRST - CHECKS IF THERE IS A T ARGUMENT
TYPE=""
while getopts "t:" opt; do
case $opt in
t) TYPE="$OPTARG"
;;
esac
done
if [ -z "$TYPE" ]; then
echo "No -t. Bye."
exit 1 # error
else
echo "Valid -t!"
exit 0 # error
fi
SECOND - CHECKS IF T FLAG IS VALID (img, image or doc)
t)
case $OPTARG in
img|image|doc)
TYPE="$OPTARG"
;;
*)
echo >&2 "Unsupported type..."
exit 1
;;
esac
;;
For the life of me I can't combine the two and get them working. How can I get these scripts to play nice so I not only check if there a t argument, but also check if it is valid or not and print a message accordingly.
Thank you for any help you can give.
#!/bin/bash
TYPE=""
while getopts "t:" opt; do
case $opt in
t)
case $OPTARG in
img|image|doc)
TYPE="$OPTARG"
;;
*)
echo >&2 "Unsupported type..."
exit 1
;;
esac
;;
esac
done
if [ -z "$TYPE" ]; then
echo "No -t. Bye."
exit 1 # error
else
echo "Valid -t!"
exit 0 # error
fi

Resources