Getopts in bash not selecting option - bash

I have one problem , when i select one option , for exemple ./test.sh -f it should print "mel" but it reads all code.
How does it enter the if condition and passes with other argument ?
if getopts :f:d:c:v: arg ; then
if [[ "${arg}" == d ]] ; then
d_ID=$OPTARG
eval d_SIZE=\$$OPTIND
else
echo "Option -d argument missing: needs 2 args"
echo "Please enter two args: <arg1> <arg2>"
read d_ID d_SIZE
echo "disc $d_ID $d_SIZE" >> $FILENAME
fi
if [[ "${arg}" == c ]] ; then
c_NOME="$OPTARG"
eval c_ID1=\$$OPTIND
eval c_ID2=\$$OPTINDplus1
eval c_FICHEIRO=\$$OPTINDplus2
else
echo "Option -c argument missing: needs 4 args"
echo "Please enter two args: <arg1> <arg2> <arg3> <agr4>"
read c_NOME c_ID1 c_ID2 c_FICHEIRO
echo "raidvss $c_NOME $c_ID1 $c_ID2 $c_FICHEIRO" >> $FILENAME
fi
if [[ "${arg}" == f ]] ; then
echo "mel"
fi
fi

You are using getopts parameters wrong.
if getopts :f:d:c:v: arg
means that -f will follow the value of parameter, like
-f 5
If you want just have -f (without value) you need to change it to
if getopts :fd:c:v: arg ; then
(I deleted the ':'). Also, I think you should better use while cycle and case statements.
See this example
while getopts fd:c:v: opt
do
case "$opt" in
f) echo "mel";;
d) discFunction "$OPTARG";;
c) otherFunction "$OPTARG";;
v) nop;;
\?) echo "$USAGE" >&2; exit 2;;
esac
done
shift `expr $OPTIND - 1`

Related

Bash Shell Script: How can I restrict getopts so that it only takes one type of option?

I am using getopts and for the options I want to accept just one type of letter but it can be passed multiple times. Can't figure out how to do this but it should work the way ls -l does where that ls -lllllll and ls -l -l -l -l -l all return the same thing and it only runs once.
while getopts ":abc" opt; do
case "$opt" in
a) echo "a"
;;
b) echo "b"
;;
p) echo "c"
;;
?) echo "error"
;;
esac
done
so in this example, I want ./program.sh -a, ./program.sh -aaaaaaa (with any number of as), and ./program.sh -a -a -a -a to all return "a" just one time and then something like ./program.sh -ab or ./program.sh -abc or ./program.sh -a -c to return an error
Don't take action while parsing your options. Just record which options are seen, and take action afterwards.
while getopts ":abc" opt; do
case "$opt" in
a) A=1
;;
b) B=1
;;
c) C=1
;;
?) echo "error"
;;
esac
done
if ((A + B + C > 1)); then
printf 'Only one of -a, -b, -c should be used.\n' >&2
exit 1;
fi
[[ $A == 1 ]] && echo "a"
[[ $B == 1 ]] && echo "b"
[[ $C == 1 ]] && echo "c"
You have to write the logic yourself to handle multiple invocations of the same argument.
A=0
while getopts ":a" option; do
case "$option" in
a) [[ $A != 1 ]] && echo "a"
A=1
;;
*) echo "error"
;;
esac
done

How to pass a long option to a bash script?

./script.sh -abc hello
How can I write my script to use '-abc' as the option and 'hello' as the value to that option?
I should be able to pass this value to all the functions in this script. Lets say I have 2 functions: X and Y.
Use this in your script:
[[ $1 == -abc ]] && value="$2" || echo invalid option
If you don't want to print any messages on wrong option or no option, then omit the || echo ... part, value will be empty.
If you want to make the second argument a must, then:
[[ $1 == -abc ]] && [[ $2 != "" ]] && value="$2" || echo invalid option
Using if else loop will give you complete control over this:
if [[ $1 == -abc ]]; then
#if first option is valid then do something here
if [[ $2 != "" ]]; then
value="$2"
else
#if second option is not given then do something here
echo invalid option
fi
else
echo invalid option
#if first option is invalid then do something here
fi
If you want to make the first argument a must too, then change the first if statement line to
if [[ $1 == -abc && $1 != "" ]]; then
If you want to pass as many arguments as you wish and process them,
then use something like this:
#!/bin/bash
opts=( "$#" )
#if no argument is passed this for loop will be skipped
for ((i=0;i<$#;i++));do
case "${opts[$i]}" in
-abc)
# "${opts[$((i+1))]}" is the immediately follwing option
[[ "${opts[$((i+1))]}" != "" ]] &&
value="${opts[$((i+1))]}"
echo "$value"
((i++))
#skips the nex adjacent argument as it is already taken
;;
-h)
#dummy help option
echo "Options are [-abc value], -h"
;;
*)
#other unknown options
echo invalid option
break
;;
esac
done
This is an example of handling multiple arguments with only two options available -abc value and -h
bash doesn't have a built in command for processing long arguments. In order to parse long options in a shell script, you'll need to iterate over the arguments list yourself.
Here's one approach:
#!/bin/sh
is_option_arg () {
case $1 in
-*)
return 1
;;
*)
return 0
;;
esac
}
usage () {
echo "$(basename "$0") -abc ARG -def ARG -verbose"
}
OPT_ABC=
OPT_DEF=
OPT_VERBOSE=false
while [ "$#" -gt 0 ]; do
case $1 in
-abc)
shift
{ [ "$#" -ne 0 ] && is_option_arg "$1"; } || { usage >&2; exit 1; }
OPT_ABC=$1
;;
-def)
shift
{ [ "$#" -ne 0 ] && is_option_arg "$1"; } || { usage >&2; exit 1; }
OPT_DEF=$1
;;
-verbose)
OPT_VERBOSE=true
;;
*)
break
;;
esac
shift
done
echo "OPT_ABC=$OPT_ABC"
echo "OPT_DEF=$OPT_DEF"
echo "OPT_VERBOSE=$OPT_VERBOSE"
if [ "$#" -gt 0 ]; then
echo "Remaining args:"
for arg in "$#"; do
echo "$arg"
done
fi
You pretty much have to implement it yourself manually. Here's one way:
abc=
while [[ "$1" == -* ]]; do
opt=$1
shift
case "$opt" in
-abc)
if (( ! $# )); then
echo >&2 "$0: option $opt requires an argument."
exit 1
fi
abc="$1"
shift
;;
*)
echo >&2 "$0: unrecognized option $opt."
exit 2
;;
esac
done
echo "abc is '$abc', remaining args: $*"
Some sample runs of the above:
(0)$ ./script.sh
abc is '', remaining args:
(0)$ ./script.sh hello
abc is '', remaining args: hello
(0)$ ./script.sh -abc hello
abc is 'hello', remaining args:
(0)$ ./script.sh -abc hello there
abc is 'hello', remaining args: there
(0)$ ./script.sh -abc
./script.sh: option -abc requires an argument.
(1)$ ./script.sh -bcd
./script.sh: unrecognized option -bcd.
(2)$

GETOPTS case with no optional arguments

Hello I'm trying to find a way how to make getopts work with non of expected optional arguments
I have a script with optional arguments
script.sh [-a] [-b] [-c | -d] file
I have it working with -a..-d like this
while geopts abc:abd opt
do
case $opt in
a) do this ;;
b) do this ;;
...
.. etc
I want to make it so it can work without those arguments, so I can run it like this
script.sh file
Is there a way to make a new case option or do I need to do it other way, thanks for all help, im a beginner in bash.
I've done this kind of thing before:
declare -A have=([a]=false [b]=false [c]=false [d]=false)
while geopts :abcd opt; do
case $opt in
a) have[a]=true ;;
b) have[b]=true ;;
c) have[c]=true ;;
d) have[d]=true ;;
?) echo "illegal option: -$OPTARG"; exit 1;;
esac
done
shift $((OPTIND-1))
if ${have[c]} && ${have[d]}; then
echo "cannot give both -c and -d"
exit 1
fi
${have[a]} && do_a_stuff
${have[b]} && do_b_stuff
...
That case statement is a pretty egregious bit of cut'n'paste programming: tightening it up:
while geopts :abcd opt; do
case $opt in
a|b|c|d) have[$opt]=true ;;
?) echo "illegal option: -$OPTARG"; exit 1;;
esac
done
I have the following test script:
#! /bin/bash
USAGE="test.sh [-a] [-b] [-c | -d ]"
while getopts :abcd option
do
case $option in
a) OPT_A=1;;
b) OPT_B=1;;
c) OPT_C=1;;
d) OPT_D=1;;
*)
echo "$OPTARG is not a valid option."
echo "$USAGE"
exit 2;;
esac
done
shift $((OPTIND-1))
if [[ $OPT_C && $OPT_D ]]
then
echo "That's a no-no using options c and d together"
echo "$USAGE"
exit 2
fi
echo "The following options were set"
[[ $OPT_A ]] && echo " Option A was set"
[[ $OPT_B ]] && echo " Option B was set"
[[ $OPT_C ]] && echo " Option C was set"
[[ $OPT_D ]] && echo " Option D was set"
echo "And the file name is $1"
It will show you what options were set (and which ones weren't). It tests to make sure that -c and -d aren't used together.
Not 100% sure what you're asking for. But, you can see that instead of saying do this in my case statement, I'm merely setting variables that show what options were or weren't selected. That in itself my solve your problem.

How to create a bash script with optional parameters for a flag

I'm trying to create a script which will have a flag with optional options. With getopts it's possible to specify a mandatory argument (using a colon) after the flag, but I want to keep it optional.
It will be something like this:
./install.sh -a 3
or
./install.sh -a3
where 'a' is the flag and '3' is the optional parameter that follows a.
Thanks in advance.
The getopt external program allows options to have a single optional argument by adding a double-colon to the option name.
# Based on a longer example in getopt-parse.bash, included with
# getopt
TEMP=$(getopt -o a:: -- "$#")
eval set -- "$TEMP"
while true ; do
case "$1" in
-a)
case "$2" in
"") echo "Option a, no argument"; shift 2 ;;
*) echo "Option a, argument $2"; shift 2;;
esac ;;
--) shift; break ;;
*) echo "Internal error!"; exit 1 ;;
esac
done
The following is without getopt and it takes an optional argument with the -a flag:
for WORD; do
case $WORD in
-a?) echo "single arg Option"
SEP=${WORD:2:1}
echo $SEP
shift ;;
-a) echo "split arg Option"
if [[ ${2:0:1} != "-" && ${2:0:1} != ""]] ; then
SEP=$2
shift 2
echo "arg present"
echo $SEP
else
echo "optional arg omitted"
fi ;;
-a*) echo "arg Option"
SEP=${WORD:2}
echo $SEP
shift ;;
-*) echo "Unrecognized Short Option"
echo "Unrecognized argument"
;;
esac
done
Other options/flags also can be added easily.
Use the getopt feature. On most systems, man getopt will yield documentation for it, and even examples of using it in a script. From the man page on my system:
The following code fragment shows how one might process the arguments
for a command that can take the options -a and -b, and the option -o,
which requires an argument.
args=`getopt abo: $*`
# you should not use `getopt abo: "$#"` since that would parse
# the arguments differently from what the set command below does.
if [ $? != 0 ]
then
echo 'Usage: ...'
exit 2
fi
set -- $args
# You cannot use the set command with a backquoted getopt directly,
# since the exit code from getopt would be shadowed by those of set,
# which is zero by definition.
for i
do
case "$i"
in
-a|-b)
echo flag $i set; sflags="${i#-}$sflags";
shift;;
-o)
echo oarg is "'"$2"'"; oarg="$2"; shift;
shift;;
--)
shift; break;;
esac
done
echo single-char flags: "'"$sflags"'"
echo oarg is "'"$oarg"'"
This code will accept any of the following as equivalent:
cmd -aoarg file file
cmd -a -o arg file file
cmd -oarg -a file file
cmd -a -oarg -- file file
In bash there is some implicit variable:
$#: contains number of arguments for a called script/function
$0: contains names of script/function
$1: contains first argument
$2: contains second argument
...
$n: contains n-th argument
For example:
#!/bin/ksh
if [ $# -ne 2 ]
then
echo "Wrong number of argument - expected 2 : $#"
else
echo "Argument list:"
echo "\t$0"
echo "\t$1"
echo "\t$2"
fi
My solution:
#!/bin/bash
count=0
skip=0
flag="no flag"
list=($#) #put args in array
for arg in $# ; do #iterate over array
count=$(($count+1)) #update counter
if [ $skip -eq 1 ]; then #check if we have to skip this args
skip=0
continue
fi
opt=${arg:0:2} #get only first 2 chars as option
if [ $opt == "-a" ]; then #check if option equals "-a"
if [ $opt == $arg ] ; then #check if this is only the option or has a flag
if [ ${list[$count]:0:1} != "-" ]; then #check if next arg is an option
skip=1 #skip next arg
flag=${list[$count]} #use next arg as flag
fi
else
flag=${arg:2} #use chars after "-a" as flag
fi
fi
done
echo $flag

shell get opt is catputre $1 as argument

FILE_LIST=$1
MOVE=0
while getopts "m" OPT; do
case $OPT in
m) MOVE=1 ;;
M) MOVE=1 ;;
*) echo "Invalid parameter." >&2; exit 1 ;;
esac
done
echo $MOVE
echo $FILE_LIST
I will pass optional argument ( -m/-M) and file list .
test.sh -m a.txt
its display 1 -m , but i am looking for 1 a.txt
Supost if test.sh a.xt
it should be diplsay 0 and a.txt
You need to shift the arguments.
MOVE=0
while getopts "mM" OPT; do
case $OPT in
M|m) MOVE=1
shift;;
*) echo "Invalid parameter." >&2; exit 1 ;;
esac
done
echo $MOVE
FILE_LIST=$1
echo $FILE_LIST
You can also combine m and M into one case.
If I understand right, you want the syntax for running the script to be something like:
./scriptname [-mM] firstfile [secondfile ...]
If this is correct, none of the other answers quite work; here's how I'd do it:
#!/bin/bash
# Parse command options
MOVE=0
while getopts "mM" OPT; do
case "$OPT" in
m|M) MOVE=1 ;;
*) echo "Invalid option." >&2; exit 1 ;;
esac
done
shift $(( OPTIND-1 )) # Remove options from the argument list
# Parse command arguments
if [[ $# -eq 0 ]]; then
echo "No files specified." >&2
exit 1
fi
FILE_LIST=( "$#" ) # Use an array in case of spaces in filenames
# Some examples of things to do with the results:
# Work with the specified files individually:
for FILE in "${FILE_LIST[#]}"; do
chmod g+w "$FILE"
done
# Work with the specified files as a group:
if (( MOVE == 1 )); then
mv "${FILE_LIST[#]}" "$DEST_DIR"
else
cp "${FILE_LIST[#]}" "$DEST_DIR"
fi
I do not exactly know what you want but Here are some code examples:
First example assumes that the filelist is given always after the -m option
while getopts "m:" OPT
do
case $OPT in
m)
echo "option m"
FILE_LIST = $OPTARG
;;
*)
echo "error"
;;
esac
done
echo $FILE_LIST
Or a different approach with a filelist not related to the -m option
while getopts "m:" OPT
do
case $OPT in
m)
echo "option m"
MOVE = 1
;;
*)
echo "error"
;;
esac
done
shift $(($OPTIND - 1))
FILE_LIST = $1
echo $FILE_LIST
Hope this suits your needs
You have to use $OPTARG value for this. Notice m:. The colon specifies that there are arguments passed to -m
#!/bin/bash
MOVE=0
while getopts "m:M:" OPT; do
case $OPT in
m|M) MOVE=1
FILE_LIST="$FILE_LIST $OPTARG"
;;
*) echo "Invalid parameter." >&2; exit 1 ;;
esac
done
shift $(( OPTIND-1 ))
[[ $MOVE != 1 ]] && FILE_LIST=$1
echo $MOVE
echo $FILE_LIST

Resources