How to use getopt long option in bash script - bash

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.

Related

Bash script with named parameter containing equal sign

I am trying to pass some values to my bash script using named parameters similar to the following:
./script.sh --username='myusername' --password='superS3cret!' --domainou="OU=Groups with Space,OU=subou,DC=mydomain,DC=local"
I have the following code:
#!/bin/bash
while [ "$1" != "" ]; do
PARAM=`echo $1 | awk -vFPAT='([^=]*)|("[^"]+")' -vOFS="=" '{print $1}'`
VALUE=`echo $1 | awk -vFPAT='([^=]*)|("[^"]+")' -vOFS="=" '{print $2}'`
case $PARAM in
-u | --username)
username=$VALUE
;;
-p | --password)
password=$VALUE
;;
-ou | --domainou)
domainou=$VALUE
;;
*)
echo "ERROR: unknown parameter \"$PARAM\""
exit 1
;;
esac
shift
done
echo $username
echo "$password"
echo "$domainou"
What I get when I run my script is:
myusername
superS3cret!
OU
Now the first two lines are correct but obviously I don't want OU...
I want:
OU=Groups with Space,OU=subou,DC=mydomain,DC=local
Awk seems to be matching the = inside the quote. As best as I can tell the way to solve that is using
-vFPAT='([^=]*)|("[^"]+")' -vOFS="="
But clearly that's not working so I am just wondering if any awk gurus can help me understand what's wrong with my awk statement.
Thanks
Brad
You can do it like this:
#!/bin/bash
while [ $# -gt 0 ]; do
case "$1" in
-u=* | --username=*)
username="${1#*=}"
;;
-p=* | --password=*)
password="${1#*=}"
;;
-ou=* | --domainou=*)
domainou="${1#*=}"
;;
*)
printf "Error: unknown option: $1\n"
exit 1
esac
shift
done
printf "username: $username\n"
printf "password: $password\n"
printf "domainou: $domainou\n"
For parsing command line options that include both long and short optoins, consider using GNU getopt, which has support for long options. While it is possible to build-your-own parser replacement, using the getopt provides for more robust parsing:
Abbreviation of options (e.g., accepting --user for --username).
Checking for required/optional values
Error handling
See also: Using getopts to process long and short command line options
set $(getopt --long 'username:,password:,ou:,domain:' -o 'u:p:' -- "$0" "$#")
while [ "$#" -gt 0 ] ; do
OP=$1
shift
case "$OP" in
--) PROG=$1 ; shift ; break ;;
-u | --username) username=$1 ; shift ;;
-p | --password) password=$1 ; shift ;;
--ou | --domain) domainou=$1 ; shift ;;
esac
done
# Positional arguments are set ...
Below is what ultimately worked best for me.
#dash-o definitely got me pointed in the right direction but the script you provided was printing out extraneous info:
set: usage: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
I believe the offending line was this:
set --long 'username:,password:,ou:,domain:' -o 'u:p:' -- "$0" "$#"
Here's the code that accomplished what I needed. I can't take credit for this. I stole it from here Using getopts to process long and short command line options but I never would have found that if not for dash-o so a big thank you!
#!/bin/bash
die() { echo "$*" >&2; exit 2; } # complain to STDERR and exit with error
needs_arg() { if [ -z "$OPTARG" ]; then die "No arg for --$OPT option"; fi; }
while getopts ab:c:-: OPT; do
# support long options: https://stackoverflow.com/a/28466267/519360
if [ "$OPT" = "-" ]; then # long option: reformulate OPT and OPTARG
OPT="${OPTARG%%=*}" # extract long option name
OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty)
OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=`
fi
case "$OPT" in
u | username ) needs_arg; username="$OPTARG" ;;
p | password ) needs_arg; password="$OPTARG" ;;
o | domainou ) needs_arg; domainou="$OPTARG" ;;
??* ) die "Illegal option --$OPT" ;; # bad long option
\? ) exit 2 ;; # bad short option (error reported via getopts)
esac
done
shift $((OPTIND-1)) # remove parsed options and args from $# list
echo "$username"
echo "$password"
echo "$domainou"

Get require and optional argument in bash script

Sorry for my newbie question, I'm confused!
I want to make a bash script for some reasons.
I need to pass some arguments when running the script.
for example:
script.sh build --with-test --without-test2 --with-test3
script.sh config
script.sh config --with-test3 --without-test2
script.sh config --add this is a test
build or config is required and also other parameters are optional and the order of using argument is not important.
I wrote this code:
if [[ $# -lt 1 ]]; then
printf "build or config parameter is missing"
exit;
fi
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
build )
mode=1
shift
shift
;;
config )
mode=0
shift
shift
;;
-wt2 | --without-test2 )
wt2=0
shift
shift
;;
-wt3 | --with-test3 )
wt3=1
shift
shift
;;
-wt0 | --with-test )
wt0=1
shift
shift
;;
-add | --additional )
additional_command=$2
shift
shift
;;
-h | --help )
help
shift
shift
exit
;;
*)
echo "Missing parameter."
shift
shift
exit
;;
esac
done
But my code does not work properly, the script will run even without build and config, I cannot figure out how can I write my if statements
The use of shift twice is a mistake. You can confirm that config AND/OR build has been specified by checking the value of mode. There also looks to be an issue with additional command.
Consider moving the shift statement.
I've added some debug to your program to see what the results are:
$ script.sh --with-test --without-test2 --with-test3
additional_command=
mode=
wt0=1
wt2=
wt3=1
As you can see, the wt2 is not set, even though --without-test2 was specified.
Here's how you can fix things up a little:
Remove the shifts in the case (except for special processing)
Review the mode parameter to see if it is set ( test -z )
Code follows:
#!/bin/bash
if [[ $# -lt 1 ]]; then
printf "build or config parameter is missing"
exit;
fi
while [[ $# -gt 0 ]]
do
key="$1"
shift
case $key in
build )
mode=1
;;
config )
mode=0
;;
-wt2 | --without-test2 )
wt2=0
;;
-wt3 | --with-test3 )
wt3=1
;;
-wt0 | --with-test )
wt0=1
;;
-add | --additional )
# Get the rest of the parameters, right?
additional_command=$*
shift $# # Shift to "deplete" the rest of the params
;;
-h | --help )
help
# No need to shift if you are exiting
exit
;;
*)
echo "Missing parameter."
# No need to shift if you are exiting
exit
;;
esac
done
#Here's a little debug code that you can remove or just comment out with :
#Change first line to : cat <<EOF if you want to comment out.
cat <<EOF
additional_command=$additional_command
mode=$mode
wt0=$wt0
wt2=$wt2
wt3=$wt3
EOF
if [ -z "$mode" ] ; then
echo "Missing config or build parameter"
fi

How to use getopt with this several values?

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' --

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 | )

Bash: --help feature

Is it posseble to have a --help argument with getopts?
I'm currently using this to code the help feature:
#!/bin/bash
PROGNAME=${0##*/}
PROGVERSION=1.0
usage()
{
cat << EO
Prog description goes here.
Usage: $PROGNAME
Options:
EO
cat <<EO | column -s\& -t
-h|--help & show this output
-v|--version & show version information
EO
}
SHORTOPTS="hv"
LONGOPTS="help,version"
ARGS=$(getopt -s bash --options $SHORTOPTS \
--longoptions $LONGOPTS --name $PROGNAME -- "$#" )
eval set -- "$ARGS"
while true; do
case $1 in
-h|--help)
usage
exit 0
;;
-v|--version)
echo "$PROGVERSION"
exit 0
;;
--)
shift
break
;;
*)
shift
break
;;
esac
shift
done
The bash getopts builtin does not support long option names with the double-dash prefix. It only supports single-character options.

Resources