getopts second flag is not required? - bash

Having issues making sure that both -v and -o are both required elements but -h is not in my getopts. how would I fix this?
usage() { echo "Usage: $0 [-v <5.x|6.x>] [-o <string>]" 1>&2; exit 1; }
if ( ! getopts ":v:o:h" opt); then
echo "Usage: `basename $0` options (-v [version]) (-o [organization unit]) -h for help";
exit $E_OPTERROR;
fi
while getopts ":v:o:h" opt; do
case $opt in
v) vermajor=$OPTARG;;
o) org_unit=$OPTARG;;
h) usage;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1;;
:) echo "Option -$OPTARG requires an argument." >&2; exit 1;;
esac
done
exit

What about this?:
usage() { echo "Usage: $0 [-v <5.x|6.x>] [-o <string>]" 1>&2; exit 1; }
test=$(echo "$#" | grep "\-v" | grep "\-o")
if [[ -z "$test" ]]; then
printf "requirements not met.\n"
usage
fi
if [[ -n "$test" ]]; then
printf "requirements met.\n"
fi
Output:
bob#crunchbang:~$ ./test -v 5.0 -h
requirements not met.
Usage: ./test [-v <5.x|6.x>] [-o <string>]
bob#crunchbang:~$ ./test -v 5.0
requirements not met.
Usage: ./test [-v <5.x|6.x>] [-o <string>]
bob#crunchbang:~$ ./test -o "yoh"
requirements not met.
Usage: ./test [-v <5.x|6.x>] [-o <string>]
bob#crunchbang:~$ ./test
requirements not met.
Usage: ./test [-v <5.x|6.x>] [-o <string>]

Ended up with this
usage() { echo "Usage: $0 [-v <5.x|6.x>] [-o <string>]" 1>&2; exit 1; }
if [[ "$1" =~ "^((-{1,2})([Hh]$|[Hh][Ee][Ll][Pp])|)$" ]]; then
usage
else
while [[ $# -gt 0 ]]; do
opt="$1"; shift
current_arg="$1"
if [[ "$current_arg" =~ ^-{1,2}.* ]]; then
echo "==> WARNING: You may have left an argument blank. Double check your command."
fi
case "$opt" in
"-v"|"--version" ) vermajor="$1"; shift;;
"-o"|"--org" ) org_unit="$1"; shift;;
* ) echo "==> ERROR: Invalid option: \""$opt"\"" >&2
exit 1;;
esac
done
fi
if [[ "$vermajor" == "" || "$org_unit" == "" ]]; then
echo "ERROR: Options -v and -o require arguments." >&2; exit 1
fi
exit

Related

How can I accept long arguments using getopts in Bash?

I'm trying to have my getops function run with multiple flags and arguments but instead of short (-f style) flag, I want to accept a long one (--flag style). For example:
if [ $# -lt 1 ]; then
usage >&2
exit 1
else
while $1 "hf:" opt; do
case $opt in
h)
echo "Here is the help menu:"
usage
;;
f)
ls -l $OPTARG >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 1
;;
esac
done
fi
I would like the -h and -f to be --help and --file respectively.
How do I do that?
getopt will do this for you. It handles short options, long options, options with and without arguments, -- to end option parsing, and more.
Boilerplate usage looks like:
options=$(getopt -o hf: -l help,file: -n "$0" -- "$#") || exit
eval set -- "$options"
while [[ $1 != -- ]]; do
case $1 in
-h|--help) echo "help!"; shift 1;;
-f|--file) echo "file! $2"; shift 2;;
*) echo "bad option: $1" >&2; exit 1;;
esac
done
shift
# Process non-option arguments.
for arg; do
echo "arg! $arg"
done

How to require command line parameters in bash?

I have a bash script where I need to have some parameters. Usage should be only between:
./script.sh --scan [scan type] [keyword]
or
./script.sh --help
In example it should be something like this:
$ ./script.sh
[Usage]
$ ./script.sh --scan
Specify scan type
$ ./script.sh --help --scan
[Usage]
$ ./script.sh --scan short
Specify keyword to search
$ ./script.sh --scan short keyword
[Starts short scanning for "keyword" - go to function where my script is, blah, blah]
$ ./script.sh keyword --scan short
[As above]
$ ./script.sh keyword
[Usage]
How I can achieve this?
This is just off the top of my head:
if [ "$1" = "--scan" ] ; then
if [ "$#" -ge 2 ] ; then
_SCANTYPE=$2
if [ "$#" -ge 3 ] ; then
_KEYWORD=$3
else
echo -n "Specify keyword to search: "
read _KEYWORD
fi
else
echo -n "Specify scan type: "
read _SCANTYPE
echo -n "Specify keyword to search: "
read _KEYWORD
fi
# Actual scan code
else
# Display usage info
fi
This is pretty basic, of course, but hopefully it will get you started.
Almost any Configure script to build a piece of software from source will have what you want. From perl's:
: option parsing
while test $# -gt 0; do
case "$1" in
-d) shift; fastread=yes;;
-e) shift; alldone=cont;;
-f)
shift
cd ..
if test -r "$1"; then
config_sh="$1"
else
echo "$me: cannot read config file $1." >&2
error=true
fi
cd UU
shift;;
--help|\
-h) shift; error=true;;
-r) shift; reuseval=true;;
-s) shift; silent=true; realsilent=true;;
-E) shift; alldone=exit;;
-K) shift; knowitall=true;;
-O) shift; override=true;;
-S) shift; silent=true; extractsh=true;;
-D)
shift
case "$1" in
*=)
echo "$me: use '-U symbol=', not '-D symbol='." >&2
echo "$me: ignoring -D $1" >&2
;;
*=*) echo "$1" | \
sed -e "s/'/'\"'\"'/g" -e "s/=\(.*\)/='\1'/" >> optdef.sh;;
*) echo "$1='define'" >> optdef.sh;;
esac
shift
;;
-U)
shift
case "$1" in
*=) echo "$1" >> optdef.sh;;
*=*)
echo "$me: use '-D symbol=val', not '-U symbol=val'." >&2
echo "$me: ignoring -U $1" >&2
;;
*) echo "$1='undef'" >> optdef.sh;;
esac
shift
;;
-A)
shift
xxx=''
yyy="$1"
zzz=''
uuu=undef
case "$yyy" in
*=*) zzz=`echo "$yyy"|sed 's!=.*!!'`
case "$zzz" in
*:*) zzz='' ;;
*) xxx=append
zzz=" "`echo "$yyy"|sed 's!^[^=]*=!!'`
yyy=`echo "$yyy"|sed 's!=.*!!'` ;;
esac
;;
esac
case "$xxx" in
'') case "$yyy" in
*:*) xxx=`echo "$yyy"|sed 's!:.*!!'`
yyy=`echo "$yyy"|sed 's!^[^:]*:!!'`
zzz=`echo "$yyy"|sed 's!^[^=]*=!!'`
yyy=`echo "$yyy"|sed 's!=.*!!'` ;;
*) xxx=`echo "$yyy"|sed 's!:.*!!'`
yyy=`echo "$yyy"|sed 's!^[^:]*:!!'` ;;
esac
;;
esac
case "$xxx" in
append)
echo "$yyy=\"\${$yyy}$zzz\"" >> posthint.sh ;;
clear)
echo "$yyy=''" >> posthint.sh ;;
define)
case "$zzz" in
'') zzz=define ;;
esac
echo "$yyy='$zzz'" >> posthint.sh ;;
eval)
echo "eval \"$yyy=$zzz\"" >> posthint.sh ;;
prepend)
echo "$yyy=\"$zzz\${$yyy}\"" >> posthint.sh ;;
undef)
case "$zzz" in
'') zzz="$uuu" ;;
esac
echo "$yyy=$zzz" >> posthint.sh ;;
*) echo "$me: unknown -A command '$xxx', ignoring -A $1" >&2 ;;
esac
shift
;;
-V) echo "$me generated by metaconfig 3.5 PL0." >&2
exit 0;;
--) break;;
-*) echo "$me: unknown option $1" >&2; shift; error=true;;
*) break;;
esac
done
For your two examples
./script.sh --scan [scan type] [keyword]
or
./script.sh --help
Try this
#!/bin/bash
usageMsg="usage:ScriptName --scan [scan type] [keyword] OR --help"
case $# in
[123] ) : nothing_may_be_OK ;;
0 ) # no args, display usageMsg
echo "$usageMsg" >&2 ; exit 1 ;;
esac
case "$1" in
--[Hh][Ee][Ll][Pp] ) echo "$usageMsg" >&2 ; exit 1 ;;
--[Ss][Cc][Aa][Nn] )
shift
case $# in
0) # using defaults
scanType="PrimaryScan" # change to your default scanType
keyword="PrimaryKeyWord" # again, change to your default value
;;
1)
echo "$usageMsg" >&2
echo "must provide both [scan type] and [keyword] arguments. Cant continue" >&2
exit 1
;;
2)
scanType="$1"
keyword="$2"
;;
esac # case $#
;;
esac # case $1
#restof your code goes here
echo "running scanner with scanType=$scanType and keyWord=$keyword"
echo ". . . . ."
exit 0
I'm guessing that your usage pattern means --scan OR advanced scan (with details), i.e. --scan [scan type] [keyword]
If you really intend for user to optionally add [scan type] AND/OR [keyword] then you'll need some further use of the techniques in the code above.
IHTH
I finally managed that! The basic version is:
#!/bin/bash
function usage
{
echo "Usage: usage"
exit
}
while [ $# -gt 0 ]; do
case $1 in
-h|--help)
usage
;;
-s|--scan)
scantype="$2"
shift
;;
-*)
usage
;;
*)
keyword="$1"
;;
esac
shift
done
if [ -z "$scantype" ]; then
echo "no scantype"
exit
fi
if [[ "$scantype" != "full" && "$scantype" != "long" && "$scantype" != "extended" && "$scantype" != "short" ]]; then
echo "invalid scantype"
exit
fi
if [ -z "$keyword" ]; then
echo "nokeyword"
exit
fi
echo "scantype: $scantype"
echo "keyword: $keyword"
Anyway thanks for your help :)

Why is the 3rd argument not parsed in getopts bash

I am trying to write a simple script to do something depending on the input arguments. Here is what I have written
#!/bin/bash
usage() { echo "Usage: $0 [-l <string>] [-p <string>] [-d <string> ]" 1>&2; exit 1; }
while getopts ":l:p:d" o; do
case "${o}" in
l)
l=${OPTARG}
echo "$l"
;;
p)
p=${OPTARG}
echo "$p"
;;
d)
d=${OPTARG}
echo "$d"
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${l}" ] && [ -z "${p}" ] && [ -z "${d}" ]; then
usage
fi
Its able to parse the inputs given as -l and -p properly but the third input -d is not parsed.
Output:
sagar#CPU:~/$ ./play.sh -l "l is parsed" -p "p is parsed" -d "d is parsed"
l is parsed
p is parsed
This works
sagar#CPU:~/$ ./play.sh -p "p is parsed"
p is parsed
Whereas this doesn't work
sagar#CPU:~/$ ./play.sh -d "d is parsed"
Usage: ./play.sh [-l <song name>] [-p <song name>] [-d <song name> ]
What am I doing wrong here?
Thanks for your help in advance.
You are missing : in while statement passing arguments
#!/bin/bash
usage() { echo "Usage: $0 [-l <string>] [-p <string>] [-d <string> ]" 1>&2; exit 1; }
while getopts ":l:p:d:" o; do
case "${o}" in
l)
l=${OPTARG}
echo "$l"
;;
p)
p=${OPTARG}
echo "$p"
;;
d)
d=${OPTARG}
echo "$d"
;;
*)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${l}" ] && [ -z "${p}" ] && [ -z "${d}" ]; then
usage
fi
Run :
./script.sh -l "l is parsed" -p "p is parsed" -d "d is parsed"
Output :
l is parsed
p is parsed
d is parsed
Run :
./script.sh -d "d is parsed"
Output :
d is parsed

Bash script and getopts outputting saved variables

I'm trying to put together a script to monitor files and im having a hard tiem with getopts. If I run more than once the shell values dont changes... There's a ton of clean up but I've been stuck on this one issue for a couple hours so ...
#!/bin/bash
function printUsage {
echo "Usage: $0 [-e environment] [-r region] [-l logtype]"
echo "Requirements:"
echo " -e environment to look in; prod or dev"
echo " -r 3 digit region designation"
echo " -l log type to find; soap, message, main, service, or detail"
echo " example: $0 -e dev -r 111 -f soap"
exit 0
}
if [ $# -lt 1 ]; then
printUsage
fi
while getopts "e:r:l:" opt; do
case $opt in
e)
environment="$OPTARG"
;;
r)
region="$OPTARG"
;;
l)
logtype="$OPTARG"
;;
\?)
echo "Invalid option: -$OPTARG"
printUsage;;
:)
echo "-$OPTARG requires an argument"
printUsage;;
esac
done
#build array of directories to search based on argument
function buildProductionArray {
# assume these are here
logpaths=( )
}
#build array of directories to search based on argument
function buildDevArray {
# assume these are here
logpaths=( )
}
function validateArguments {
#validate the environment
if [ "$(echo "$environment" | tr "[:lower:]" "[:upper:]")" == "PROD" ]; then
buildProductionArray
elif [ "$(echo "$environment" | tr "[:lower:]" "[:upper:]")" == "DEV" ]; then
buildDevArray
else
echo "Usage: findlog [-e environment] [-r region] [-l logtype]"
echo "Invalid environment" >&2; exit 1
fi
#validate the region
if ! [ "$region" -eq "$region" ] 2>/dev/null; then
echo "Usage: findlog [-e environment] [-r region] [-l logtype]"
echo "Invalid region" >&2; exit 1
fi
#validate the logtype
function in_array() {
elements=${1}
element=${2}
for i in "${elements[#]}" ; do
if [ "$i" == "$element" ] ; then
return 1
fi
done
return 0
}
logvalues=(soap message service main detail)
log="$(echo "$logtype" | tr '[:upper:]' '[:lower:]')"
echo "$log"
if in_array logvalues "${log}"; then
if [[ "$log" == "service" || "$log" == "detail" ]]; then
log="-$log-time-"
else
log="-$log-"
fi
else
echo "Usage: findlog [-e environment] [-r region] [-l logtype]"
echo "Invalid environment" >&2; exit 1
fi
}
validateArguments
for i in "${logpaths[#]}"
do
#assume this is here
done

Echo Usage if they don't put parameters correct

I have this
USAGE="Usage: -f [file name] -c [column] -v [value] ."
while getopts ":f:c:v: " OPTIONS; do
case $OPTIONS in
f ) file=$OPTARG;;
c ) column=$OPTARG;;
v ) value=$OPTARG;;
h ) echo $USAGE;;
\? ) echo $USAGE
exit 1;;
* ) echo $USAGE
exit 1;;
esac
done
the filename is fun2.sh ... I want to echo the $USAGE if they fail to put a parameter in -f or -c or -v.
I have tryied putting a
" ") echo $USAGE
exit1;;
but that didn't work..
I also tried
if [ $file || $column || $value == "" ]
echo $USAGE
but then again it didn't work..
Any Help would be appreciated
EDIT
What worked for me
if [ "$file" == "" ] ;
then
echo $USAGE
elif [ "$column" == "" ];
then
echo $USAGE
elif [ "$value" == "" ];
then
echo $USAGE
else
show_column
check_temp
file_move
write_file
Try:
[[ -z "$file" || -z "$column" || -z "$value" ]] && echo "$USAGE" && exit
You can't do this in the loop. Check the values of the variables after the loop and print $USAGE if they are empty or the values are wrong (not an integer, for example).
Not entirely sure why your code doesn't work, but this should:
USAGE="Usage: -f [file name] -c [column] -v [value] ."
params="$(getopt -o f:c:v:h --name "$(basename -- "$0")" -- "$#")"
if [ $? -ne 0 ]
then
echo "$USAGE"
exit 1
fi
eval set -- "$params"
while true
do
case $1 in
-f)
file="$2"
shift 2
;;
-c)
column="$2"
shift 2
;;
-v)
value="$2"
shift 2
;;
-h)
echo "$USAGE"
exit
;;
--)
shift
break
;;
*)
echo "$USAGE"
exit 1
;;
esac
done
John, i dont understand what is a bad with your code: In my bash got the following: (the name of script is "go") - OK mean, got what i expected.
jonatan:shell clt$ ./go
#ok
jonatan:shell clt$ ./go ewdwedw
#ok
jonatan:shell clt$ ./go -a
Usage: -f [file name] -c [column] -v [value] .
#ok, -a is incorrect
jonatan:shell clt$ ./go -f
Usage: -f [file name] -c [column] -v [value] .
#ok, -f need an argument
jonatan:shell clt$ ./go -f aaa
#ok, -f has an argiment
jonatan:shell clt$ ./go -c
Usage: -f [file name] -c [column] -v [value] .
jonatan:shell clt$ ./go -c -v
#hm - "-v" comes into `column`, so syntactically is ok, but ....
Therefore you need another check - as you done already. So, your script is OK.
Here is my "go".
#!/bin/bash
USAGE="Usage: -f [file name] -c [column] -v [value] ."
while getopts ":f:c:v:" OPTIONS; do
case "$OPTIONS" in
f ) file=$OPTARG;;
c ) column=$OPTARG;;
v ) value=$OPTARG;;
h ) echo $USAGE;;
\? ) echo $USAGE; exit 1;;
* ) echo $USAGE; exit 1;;
esac
done

Resources