Bash long options/flags - how to do it? - bash

I am trying to change my working script with getopts to getopt ( long flags ).
Below i present my code which is working.
getopts 'm:' mode
modeValue=$OPTARG
getopts 'p:' parameter
parameterValue=$OPTARG
getopts 'u:' parameter
parameterValue2=$OPTARG
getopts 'l:' parameter
parameterValue3=$OPTARG
getopts 'n:' parameter
parameterValue4=$OPTARG
getopts 'e:' parameter
parameterValue5=$OPTARG
getopts 'w:' parameter
parameterValue6=$OPTARG
getopts 'r:' parameter
parameterValue7=$OPTARG
case $modeValue in
addRepository)
doAddRepository "$parameterValue" "$parameterValue7"
exit $?
;;
addProject)
doAddProject "$parameterValue"
exit $?
;;
addUser)
doAddUser "$parameterValue2" "$parameterValue4" "$parameterValue5" "$parameterValue6"
exit $?
;;
assignProject)
doAssignProject "$parameterValue" "$parameterValue2" "$parameterValue3"
exit $?
;;
*)
#echo "$doShowUsage"
exit 1
;;
esac
Now my script is working like example below:
For add repository: ./script.sh -m addRepository -p NameOfTheProject -r NameOfTheRepository
I want to edit this for something like this:
./script.sh --mode addRepository --project NameOfTheProject --repo NameOfTheRepository
I started to modify code and added something what i present below:
TEMP=`getopt -o m:p:u:l:n:e:c:r: --long mode:,project:,username:,level:,name:,email:,pass:,repo: -n 'test.sh'
-- "$#"` eval set -- "$TEMP"
while true ; do
case "$1" in
-m|--mode)
case "$2" in
addRepository)
doAddRepository=$2 ; shift 2 ;;
addProject)
doAddProject=$2 ; shift 2 ;;
addUser)
doAddUser=$2 ; shift 2 ;;
assignProject)
doAssignProject=$2 ; shift 2 ;;
esac ;;
-h|--help)
case "$2" in
*) echo "$doShowUsage"
exit 1
esac ;;
esac done
My question is : Am I doing it in the right way ? How can I add parameters to the functions "doAddProject/Repository/User...?" Can someone give me some advices? Above functions got different amount of parameters so take a look at it.
Thank you!

Stephane Chazelas wrote a very fine getops-long shell script that I use in my bash debugger. You can copy that script and use it.
If you run that program setting variable test_getopts_long, e.g.
test_getopts_long=1 bash getopts_long.sh
you'll see extensive examples for how to use, and it tests itself.

Related

Update parameter with default value in a bash script [duplicate]

I'm trying to make a getopt command such that when I pass the "-ab" parameter to a script,
that script will treat -ab as a single parameter.
#!/bin/sh
args=`getopt "ab":fc:d $*`
set -- $args
for i in $args
do
case "$i" in
-ab) shift;echo "You typed ab $1.";shift;;
-c) shift;echo "You typed a c $1";shift;;
esac
done
However, this does not seem to work. Can anyone offer any assistance?
getopt doesn't support what you are looking for. You can either use single-letter (-a) or long options (--long). Something like -ab is treated the same way as -a b: as option a with argument b. Note that long options are prefixed by two dashes.
i was struggling with this for long - then i got into reading about getopt and getopts
single char options and long options .
I had similar requirement where i needed to have number of multichar input arguments.
so , i came up with this - it worked in my case - hope this helps you
function show_help {
echo "usage: $BASH_SOURCE --input1 <input1> --input2 <input2> --input3 <input3>"
echo " --input1 - is input 1 ."
echo " --input2 - is input 2 ."
echo " --input3 - is input 3 ."
}
# Read command line options
ARGUMENT_LIST=(
"input1"
"input2"
"input3"
)
# read arguments
opts=$(getopt \
--longoptions "$(printf "%s:," "${ARGUMENT_LIST[#]}")" \
--name "$(basename "$0")" \
--options "" \
-- "$#"
)
echo $opts
eval set --$opts
while true; do
case "$1" in
h)
show_help
exit 0
;;
--input1)
shift
empId=$1
;;
--input2)
shift
fromDate=$1
;;
--input3)
shift
toDate=$1
;;
--)
shift
break
;;
esac
shift
done
Note - I have added help function as per my requirement, you can remove it if not needed
That's not the unix way, though some do it e.g. java -cp classpath.
Hack: instead of -ab arg, have -b arg and a dummy option -a.
That way, -ab arg does what you want. (-b arg will too; hopefully that's not a bug, but a shortcut feature...).
The only change is your line:
-ab) shift;echo "You typed ab $1.";shift;;
becomes
-b) shift;echo "You typed ab $1.";shift;;
GNU getopt have --alternative option
-a, --alternative
Allow long options to start with a single '-'.
Example:
#!/usr/bin/env bash
SOPT='a:b'
LOPT='ab:'
OPTS=$(getopt -q -a \
--options ${SOPT} \
--longoptions ${LOPT} \
--name "$(basename "$0")" \
-- "$#"
)
if [[ $? > 0 ]]; then
exit 2
fi
A=
B=false
AB=
eval set -- $OPTS
while [[ $# > 0 ]]; do
case ${1} in
-a) A=$2 && shift ;;
-b) B=true ;;
--ab) AB=$2 && shift ;;
--) ;;
*) ;;
esac
shift
done
printf "Params:\n A=%s\n B=%s\n AB=%s\n" "${A}" "${B}" "${AB}"
$ ./test.sh -a aaa -b -ab=test
Params:
A=aaa
B=true
AB=test
getopt supports long format. You can search SO for such examples.
See here, for example

How to handle errors in getopt

Based on tutorials I found here and here getopt should provide me with information about errors using some combination of characters :?*.
But when I used this code:
#!/bin/bash
eval set -- "$(getopt -o hspna: --long help,server,project,name-prefix,action: -- "$#")"
while [ : ]; do
case "$1" in
-s | --server)
echo "Setting server"
shift
;;
-p | --project)
echo "Setting project"
shift
;;
-n | --name-prefix)
echo "Setting name prefix"
shift
;;
-a | --action)
echo "Setting action"
shift
;;
--)
shift
break
;;
-h | --help)
echo "Providing help 1"
exit
;;
:)
echo "Providing help 2"
exit
;;
?)
echo "Providing help 3"
exit
;;
*)
echo "Providing help 4"
exit
;;
esac
done
echo $#
echo "Configured"
exit
Then following command that was supposed to show an error gave me the following output:
$ ./debug.sh -a -s -b -- foo bar baz
getopt: invalid option -- 'b'
Setting action
Setting server
foo bar baz
Configured
I was expecting that:
Providing help 2 will appear due to -a missing a value
Providing help 3 will appear due to -b not being a valid parameter
Providing help 4 will appear due to overall errors
Configured should never appear since the previous 3 points have an exit
But none of the above was true.
Also when testing further even more things did not work as expected.
# Expecting error due to missing value for `-a` but instead everything worked fine
$ ./debug.sh -a -s
Setting action
Setting server
Configured
# This time I expected everything to work fine, since I provided `X` as value of `-a`, but error was shown.
$ ./debug.sh -aX
Setting action
Providing help 3
What am I doing wrong?
What am I doing wrong?
Util-linux getopt prints and handles errors.
if ! args="$(getopt \
-n your_command \
-o hspna: \
--long help,server,project,name-prefix,action: \
-- "$#"\
)"; then
exit 1
fi
eval "set -- $args"
...
$ ./util -a
your_command: option requires an argument -- 'a'
I was expecting that:
I do not understand why. There is no such documentation in getopt. No, getopt will not output ? nor :. You can handle your (as the author of the program) errors, like you forgot to handle the option in case that you have given to getopt - you handle that with *).
The ? is a glob that matches any character. Because you forgot a shift after esac before done, X remains in $1, which is one character and is matched by ?). You meant '?'). This should go into *) case, and you should print yourself an error message.
Example, subjective in my style that I use (many people do not like set -eu):
set -euo pipefail
args=$(getopt -o ab -- "$#")
eval "set -- $args"
aflag=0
while (($#)); do
case "$1" in
-a) afloag=1; ;;
--) shift; break;
*) echo "Och no, I forgot about -b, or some other error!" >&2; exit 1; ;;
easc
shift
done

Pass options received in bash script to a called script, command or builtin

I have a bash script myscript.sh.
I mean to call another script, command or builtin from within it, e.g., diff.
I mean to pass options to myscript.sh, some of which would be passed to diff when calling it.
The way I implemented this is by setting up an option string optstring via getopt, and then using
eval "diff ${optstring} ${file} ${TRG_DIR}/${filebase2}"
So far, it worked, but I do not know if this is prone to issues when passing arguments with wildcards, etc. At any rate, ...
Is there a better way to do it?
The way I set up optstring is
set -o errexit -o noclobber -o nounset -o pipefail
params="$(getopt -o qy --long brief,side-by-side,suppress-common-lines --name "$0" -- "$#")"
if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi
echo params=$params
echo params=$#
eval set -- "$params"
optstring=""
# These variables are likely not needed
brief=false
sbs=false
scl=false
#while false ; do
while true ; do
case "$1" in
-q|--brief)
optstring=${optstring}" -q"
brief=true
echo "brief"
shift
;;
-y|--side-by-side)
optstring=${optstring}" -y"
sbs=true
echo "side-by-side"
shift
;;
--suppress-common-lines)
optstring=${optstring}" --suppress-common-lines"
scl=true
echo "suppress-common-lines"
shift
;;
--)
shift
break
;;
*)
echo "Not implemented: $1" >&2
exit 1
;;
esac
done
echo optstring=${optstring}
Use an array. Arrays can handle multi-word arguments with whitespace. Initialize a blank array with:
options=()
To append an option, do:
options+=(--suppress-common-lines)
Then finally you can get rid of the eval when you call diff and just call it normally. Make sure to quote all of the variable expansions in case they have whitespace:
diff "${options[#]}" "$file" "$TRG_DIR/$filebase2"

BASH - getopts not working properly

I'm currently having problems with my script. Basically, what I want to happen is when I execute ./apache_new_vhost.sh -a -d google.com, it will create a file and directories and if I use the -r option, it should delete.
The script was able to use the functions like add_vhost. It could create a configuration and folder however the filename is empty because it could not read the value I passed to $domain.
while getopts ":a:r:d:h" opt; do
case $opt in
a) action=add_vhost
;;
r) action=remove_vhost
;;
d) domain=$OPTARG
;;
h) usage
exit 1
;;
\?) echo "Invalid option: -$OPTARG"
usage
exit 1
;;
:) echo "Error: option -$OPTARG requires an argument."
usage
exit 1
;;
esac
done
#if [ -z $domain ]; then
# usage
# exit 1
if [ $action == "add_vhost" ]; then
echo $action $domain
elif [ $action == "remove_vhost" ]; then
echo $action $domain
fi
The options are processed in the order you specify them on the command line. So in your example, case a) is processed first, and calls your add_vhost function right then.
But the d) case hasn't been processed yet, so you haven't set domain.
You need to change your logic a bit. Rather than calling your functions directly from the case statement, save what action was selected. i.e.:
a) action="add_vhost"
;;
Then after the case, check that you do have an action selected, and call that function.
As per your script you expect argument after option -a. So when you execute your script by
./apache_new_vhost.sh -a -d google.com
then -d will consider as argument given to -a option. So your second argument discarded.To solve it just give any argument after -a (ex: ./apache_new_vhost.sh -a 1 -d google.com )option or make changes in your getopt
while getopts ":ar:d:h" opt; do

Getopts not properly parsing arguments

I have following shell script -
while getopts “h:f:p:u” OPTION
do
case $OPTION in
h)
usage
exit 1
;;
f)
FILE=$OPTARG
;;
u)
US=$OPTARG
;;
p)
PASSWD=$OPTARG
;;
?)
usage
exit
;;
esac
done
echo "$FILE"
echo "$PASSWD"
echo "$US"
I use following commandline arguments -
-u root -f mydb -p h2
There is no output on screen. Why?
Your call to getopt should look like this ...
while getopts “hf:p:u:” OPTION
... because h takes no args and the other options do.
It should be while getopts “hf:p:u:” OPTION

Resources