How to use getopt with this several values? - bash

I try to achieve a script with multi options. I started with the doc, get some errors, went to the browser. Read some links and find this on SO : Using getopts in bash shell script to get long and short command line options.
So I read it and rewrote my script. I made a mistake somewhere. Where am I wrong ?
SH
#!/bin/sh
TEMP=`getopt -o vfts: --long verbose,format,type,style: \
-n 'opt2' -- "$#"`
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$TEMP"
VERBOSE=false
FORMAT=
TYPE=
STYLE=
while true; do
case "$1" in
-v | --verbose ) VERBOSE=true; shift ;;
-f | --format ) FORMAT="$2"; shift 2 ;;
-t | --type ) TYPE="$2"; shift 2 ;;
-s | --style ) STYLE="$2"; shift 2 ;;
-- ) shift; break ;;
-*) break ;;
* ) break ;;
esac
done
echo "verbose = $VERBOSE"
echo "format = $FORMAT"
echo "type = $TYPE"
echo "style = $STYLE"
Output
> ./opt2.sh -v -f fofo -t toto -s soso
verbose = true // ok
format = -t // should be fofo
type = // should be toto
style = soso // ok

Your options string is wrong, it should be vf:t:s:. The colon indicates a required argument which each of your options except for v has. Also need to adjust your long options string accordingly.

You could have done some debugging yourself, quite easily:
$ set -- -v -f fofo -t toto -s soso
$ TEMP=$(getopt -o vfts: --long verbose,format,type,style: -- "$#")
$ echo "$TEMP"
-v -f -t -s 'soso' -- 'fofo' 'toto'
Hmm, your -f and -t arguments are disconnected. Make them required
$ TEMP=$(getopt -o vf:t:s: --long verbose,format:,type:,style: -- "$#")
$ echo "$TEMP"
-v -f 'fofo' -t 'toto' -s 'soso' --
To demonstrate that the commas apparently are not strictly required in the --long definition:
$ TEMP=$(getopt -o vf:t:s: --long verbose,format:type:style: -- "$#")
$ echo $?; echo "$TEMP"
0
-v -f 'fofo' -t 'toto' -s 'soso' --

Related

How to use getopt long option in bash script

I want to write one script something like below with the long option to be provided by user in cmd line argument.
example:
./script.sh --user username --branch branchname --start_time yyy-mm-dd
If possible ignore the order. Not sure if we can apply the logic to ignore the order.
Also, force user to provide the value otherwise throw error message that missing value.
Pasting code block
script_name=$(basename "$0")
short=u:c:e:p:b:t:n:
long=user:,component:,engagement:,product:,branch:,tag:,name:,help
TEMP=$(getopt -o $short --long $long --name "$script_name" -- "$#")
eval set -- "${TEMP}"
while :; do
case "${1}" in
-u | --user ) user="$2"; shift 2 ;;
-c | --component ) comp="$2"; COMP=$(echo $comp | tr [:upper:] [:lower:]) ; shift 2 ;;
-e | --engagement ) eng="$2"; ENG=$(echo $eng | tr [:lower:] [:upper:]) ; shift 2 ;;
-p | --product ) product="$2"; PRODUCT=$(echo $product | tr [:lower:] [:upper:]) ; shift 2 ;;
-b | --branch ) branch="$2"; shift 2 ;;
-t | --tag ) tag="$2"; shift 2 ;;
-n | --name ) name="$2"; shift 2 ;;
--help ) usage; exit 0 ;;
-- ) shift; break ;;
* ) echo "invalid option"; exit 1 ;;
esac
done
script_name=$(basename "$0")
short=u:b:s:
long=user:,branch:,start_time:,help
read -r -d '' usage <<EOF
Manually written help section here
EOF
TEMP=$(getopt -o $short --long $long --name "$script_name" -- "$#")
eval set -- "${TEMP}"
while :; do
case "${1}" in
-u | --user ) user=$2; shift 2 ;;
-b | --branch ) branch=$2; shift 2 ;;
-s | --start_time ) start_time=$2; shift 2 ;;
--help ) echo "${usage}" 1>&2; exit ;;
-- ) shift; break ;;
* ) echo "Error parsing"; exit 1 ;;
esac
done
Set your short and long options. A colon implies it needs an option argument.
Short options are written with no delimiter, long options are comma delimited.
The getopt command makes sure items come in an easily parsable order. In this example, we use a case statement to parse each option. All options are parsed first and then removed from $#.
What's left are the passed arguments that is not part of any defined option.

Parsing long command-line arguments not working with getopt

I want to parse long command-line arguments for a large script as part of my current project. I have never tried getopt before but want to try the first time to make the script look tidy.
Before trying to push getopt on that large project script, I thought of checking it first on a sample script.
In the following example script, parsing short command-line arguments work fine but not the long command-line arguments:
#!/bin/bash
options=$(getopt -o d:f:t: -l domain -l from -l to -- "$#")
[ $? -eq 0 ] || {
echo "Incorrect options provided"
exit 1
}
eval set -- "$options"
while true; do
case "$1" in
-d|--domain)
DOMAIN=$2;
shift
;;
-f|--from)
FROM=$2;
shift
;;
-t|--to)
TO=$2;
shift
;;
--)
shift
break
;;
*)
echo "Invalid options!!";
exit 1
;;
esac
shift
done
echo "Domain is $DOMAIN"
echo "From address is $FROM"
echo "To address is $TO"
exit 0;
Output:
# ./getopt_check.bash -d hello.com -f from#test.com -t to#test.com
Domain is hello.com
From address is from#test.com
To address is to#test.com
# ./getopt_check.bash --domain hello.com -f from#test.com -t to#test.com
Invalid options!!
# ./getopt_check.bash --domain hello.com --from from#test.com --to to#test.com
Invalid options!!
I am expecting the same output as well when parsing long command arguments:
Domain is hello.com
From address is from#test.com
To address is to#test.com
When debugging:
# bash -x getopt_check.bash --domain hello.com -f from#test.com -t to#test.com
++ getopt -o d:f:t: -l domain -l from -l to -- --domain hello.com -f from#test.com -t to#test.com
+ options=' --domain -f '\''from#test.com'\'' -t '\''to#test.com'\'' -- '\''hello.com'\'''
+ '[' 0 -eq 0 ']'
+ eval set -- ' --domain -f '\''from#test.com'\'' -t '\''to#test.com'\'' -- '\''hello.com'\'''
++ set -- --domain -f from#test.com -t to#test.com -- hello.com
+ true
+ case "$1" in
+ DOMAIN=-f
+ shift
+ shift
+ true
+ case "$1" in
+ echo 'Invalid options!!'
Invalid options!!
+ exit 1
Here, the issue is passing case switch OR choice -d|--domain ?.
I guess its your getopt syntax. Use :
getopt -o d:f:t: -l domain:,from:,to: -- "$#"
Instead of :
getopt -o d:f:t: -l domain -l from -l to -- "$#"

How to get multiple argumen for getopt option?

I need help to write getopt function to handle multiple argument for one option like below example, appreciate your kind support.
Example:
./getopt.sh -s abcd -s efgh -s ijkl -s bdnc -e test
This is i got so far
#!/bin/bash
OPTS=`getopt -o s:e:h -n '$0' -- "$#"`
eval set -- "$OPTS"
while true; do
case "$1" in
-s ) SOURCE=$1 ;shift ;;
-h ) echo "$0 -s source -e enviroment"; shift ;;
-e ) ENV=$1; shift; shift ;;
* ) break ;;
esac
done
if [ $ENV='TEST' ];then
echo "execute on test with $SOURCE"
elif [ $ENV='DEV' ];then
echo "execute on dev with $SOURCE"
else
exit
fi
but here I want to execute -s multiple time.
You can use the same option multiple times and add all values to an array.
like:
while getopts "o:" i; do
case $i in
o) arr+=("$OPTARG");;
#...
esac
done

How to use getopt with long options in Bash?

I have the following code in Bash:
declare {BPM_USERNAME,BPM_PASSWORD,HOST,TARGET_IP,OVERRIDE_STATUS}=''
OPTS=`getopt -a --longoptions username:,password:,csc:,ip:,override: -n "$0" -- "$#"`
eval set -- "$OPTS"
if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi
while true; do
echo ""
echo $OPTS
echo $1
echo $2
case "$1" in
--username )
BPM_USERNAME=$2
shift 2
;;
--password )
BPM_PASSWORD=$2
shift 2
;;
--csc )
HOST=$2
shift 2
;;
--ip )
TARGET_IP=$2
shift 2
;;
--override )
OVERRIDE_STATUS=$2
shift 2
;;
--)
shift
echo "Breaking While loop"
break
;;
*)
echo ""
echo "Error in given Parameters. Undefined: "
echo $*
echo ""
echo "Usage: $0 [--username BPM_USERNAME] [--password BPM_PASSWORD] [--ip IP ADDRESS_OF_VyOS/BPM] [--csc CLIENT_SHORT_CODE] [--override TRUE/FALSE]"
exit 1
esac
done
I give Bash the following command (name of script is UpdateSSL.sh):
./UpdateSSL.sh -username bpmadmin -password bpmadmin -ip 10.91.201.99 -csc xyz -override False
But instead of parsing the options, I get back the following result (showing that the while loop goes to the *) case):
'bpmadmin' --password 'bpmadmin' --ip '10.91.201.99' --csc 'xyz' --override 'False' --
bpmadmin
--password
Error in given Parameters. Undefined:
bpmadmin --password bpmadmin --ip 10.91.201.99 --csc xyz --override False --
Usage: ./UpdateSSL.sh [--username BPM_USERNAME] [--password BPM_PASSWORD] [--ip IP ADDRESS_OF_VyOS/BPM] [--csc CLIENT_SHORT_CODE] [--override TRUE/FALSE]
I don't know what I'm doing wrong.
The answer is actually at the very end of the man page:
The syntax if you do not want any short option variables at all is not very intuitive (you have to set them explicitly to the empty string).
In order to make getopt run with no short options, you have to manually specify -o '' as the first argument. I made some other changes, and the below works on my system (see *** markers):
#!/bin/bash
# *** Make sure you have a new enough getopt to handle long options (see the man page)
getopt -T &>/dev/null
if [[ $? -ne 4 ]]; then echo "Getopt is too old!" >&2 ; exit 1 ; fi
declare {BPM_USERNAME,BPM_PASSWORD,HOST,TARGET_IP,OVERRIDE_STATUS}=''
OPTS=$(getopt -o '' -a --longoptions 'username:,password:,csc:,ip:,override:' -n "$0" -- "$#")
# *** Added -o '' ; surrounted the longoptions by ''
if [[ $? -ne 0 ]] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi
# *** This has to be right after the OPTS= assignment or $? will be overwritten
set -- $OPTS
# *** As suggested by chepner
while true; do
# ... no changes in the while loop
done

command line menu options

I have been trying to get my menu to work, however it seems that it only accepts one argument/option at a time, how can I get it to accept multiple options?
#!/bin/bash
##### Main
function usage
{
echo "
usage: infinidb [[-f file ] [-i] | [-h]] name ...
}
while [ "$1" != "" ]; do
case $1 in
-f | --file ) shift
filename=$1
;;
-i | ) filename2=$2
;;
-h | --help ) usage
exit
;;
* ) usage
exit 1
esac
shift
done
echo $filename
echo $filename2
when running the script
the following output show show
./script.sh -f apple -i tree
apple tree
This works for me
#!/bin/bash
while [ "$1" != "" ]; do
case $1 in
-f | --file ) shift
filename=$1
;;
-i ) filename2=$2
;;
esac
shift
done
echo $filename
echo $filename2
There was only a problem with -i | )

Resources