I'd like to do some error checking for a bash script I am writing. In particular, I wish to ensure the following option not to be considered as the argument of an option (intentionally) left empty.
Let's say the following snippet
while getopts “hhelpc1:2:” OPTION
do
case "$OPTION" in
h|help)
usage
exit 1
;;
1)
var1=${OPTARG}
;;
2)
var2=${OPTARG}
;;
c)
test1
;;
esac
done
Assuming my script is called test.sh
By doing something like
./test.sh -1 -2 dddd -c
In the above circumstance test1 output an error message that -2 option is empty. On the opposite, I'd like to raise a warning for -1 being empty, whereas at present -2 will be taken as the argument for -1.
Any help?
Thanks
Andrea
getopts:
only handles short option names, so you cannot put "help" in your option string -- that means you're looking for "-h", "-e", "-l", "-p"
cannot look for missing arguments the way you're hoping. You'll have to examine $OPTARG to check if it looks like one of your options.
Add a leading : to the opt string to handle getopts errors yourself.
Here's a reworking of your code and I'm sure there are plenty of cases I'm not catching
#!/bin/bash
usage () { echo usage ...; }
test1 () { echo test1; }
shopt -s extglob
while getopts ":hc1:2:" opt
do
case $opt in
h)
usage
exit 1
;;
1)
case $OPTARG in
-[hc2]*)
echo "error: required argument missing for -1"
usage
exit 1
;;
*) var1=$OPTARG
;;
esac
;;
2)
case $OPTARG in
-[hc1]*)
echo "error: required argument missing for -2"
usage
exit 1
;;
*) var2=$OPTARG
;;
esac
;;
c)
test1
;;
:)
echo "error: required argument missing for -$OPTARG"
usage
exit 1
;;
\?)
# unknown argument, handle accordingly
;;
esac
done
shift $((OPTIND - 1))
echo "var1=$var1"
echo "var2=$var2"
echo "rest=$*"
Update 2014-01-23
Here's one technique:
do_test1=false
while getopts ...
case $opt in
...
c) do_test1=true ;;
...
esac
done
shift $((OPTIND - 1))
# execute function "test1" if "-c" was given:
$do_test1 && test1
Related
I have two bash scripts, do-foo-for-thing.sh and do-foo-for-many-things.sh.
do-foo-for-thing.sh takes a number of arguments, parsed using getopts, most of which are optional and have default values defined in the script.
I would like do-for-for-many-things.sh to call do-for-for-thing.sh with any of these options that have been provided, but not to use any options that have not been provided (so that they are not required for the -many-things script, and they single-thing scripts owns the default values).
I've tried to look up ways to do this, but had no luck - I'm not super familiar with bash. I can easily pass in all arguments, but that doesn't work if I don't want to provide all the potential options to the -many-things script.
Rough examples below;
do-foo-for-thing.sh
A=default-A
B=default-B
C=default-C
if [ $# -ge 1 ]; then
OPTIND=1
while getopts "a:b:c:p:" opt; do
case "$opt" in
a) A=$OPTARG ;;
B) B=$OPTARG ;;
C) C=$OPTARG ;;
P) P=$OPTARG ;;
\?) echo "Unrecognized option: -$OPTARG" >&2; exit 1 ;;
esac
done
shift $((OPTIND-1))
fi
do-foo-that-uses-values-A-B-and-C-on-P
do-foo-for-many-things.sh
if [ $# -ge 1 ]; then
OPTIND=1
while getopts "d:a:b:c:" opt; do
case "$opt" in
d) D=$OPTARG ;;
a) A=$OPTARG ;;
B) B=$OPTARG ;;
C) C=$OPTARG ;;
\?) echo "Unrecognized option: -$OPTARG" >&2; exit 1 ;;
esac
done
shift $((OPTIND-1))
fi
for P in `ls D`
do
./do-for-for-thing.sh -p P
done
This currently only passes P in. I'd like to pass in A, B, and C as well - but only if they've been provided to this script (so that any that have not been use the default values in do-foo-for-thing).
So remember the arguments and pass them along. Use bash array to properly accumulate properly quoted arguments. Use http://shellcheck.net to check your scripts.
args=()
OPTIND=1
while getopts "d:a:b:c:" opt; do
case "$opt" in
d|a|B|C) args+=(-"$opt" "$OPTARG"); ;;
\?) echo "Unrecognized option: -$OPTARG" >&2; exit 1 ;;
esac
done
shift $((OPTIND-1))
find D -maxdepth 1 -mindepth 1 -exec ./do-for-for-thing.sh "${args[#]}" -P {} "$#" \;
You have a few options here:
1. Pass through
You could simply pass the arguments through using the $# bash variable that represents all command line arguments.
./do-for-for-thing.sh -p P "$#"
2. Build argument string
Instead of:
case "$opt" in
d) D=$OPTARG ;;
a) A=$OPTARG ;;
B) B=$OPTARG ;;
C) C=$OPTARG ;;
Do
argstr=()
case "$opt" in
d) D=$OPTARG; argstr+=("-d $D ") ;;
a) A=$OPTARG; argstr+=("-a $A ") ;;
B) B=$OPTARG; argstr+=("-b $B ") ;;
C) C=$OPTARG; argstr+=("-c $C ") ;;
And then:
./do-for-for-thing.sh -p P ${argstr[#]}
Issue description :
I have 2 case statements:
first case is setting a variable in case my argument is -l
the second case should set a variable in case my argument is -P
but when i am using : ./script -l at
looks like the second case is also processed and i am getting:
./script -l at
"ERROR"
the code is :
while getopts ":l:P:h" o; do
case "${o}" in
l)
l=${OPTARG}
;;
P)
P=${OPTARG}
;;
h)
usage
;;
*)
usage
;;
esac
done
case "$l" in
"at")
l="at"
;;
"ab")
l="ab"
;;
*)
l="ac"
;;
esac
case "$P" in
"an")
P="an"
;;
"ar")
P="anr"
;;
*)
echo "ERROR"
exit 128
;;
esac
When i Use -l , i want only the first case to be matched
Your third case will throw an error if P is unassigned, or set to a value other than an or ar (which it overrides to anr). If you want to permit P to be empty, say so. If you want to require either l or P, but not both, say so.
#!/bin/bash
while getopts ":l:P:h" o; do
case "${o}" in
l)
l=${OPTARG}
;;
P)
P=${OPTARG}
;;
h) # note indentation fix
usage
;;
*)
usage
;;
esac
done
case "$l" in
"at" | "ab" ) # notice collapsing of two options, and skip of no-op code
;;
'') # notice addition of empty case
;;
*)
l="ac"
;;
esac
case "$P" in
"an")
P="an"
;;
"ar")
P="anr"
;;
*) # only fail if -l is unset
[ "$l" ] || { echo "ERROR" >&2; exit 128; }
;;
esac
# Maybe fail here if both -P and -l set?
you can try something like that.
#!bin/bash
while getopts l:P:h: option
do
case "${option}"
in
l) arg1=${OPTARG};;
P) arg2=${OPTARG};;
h) arg3=${OPTARG};;
*) echo "ERROR"
exit 128
;;
esac
done
I have a simple bash script, see below
#!/bin/bash
while getopts "t" opt; do
case $OPT in
t) JWT_TOKEN="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
;;
esac
done
echo "Token value is $JWT_TOKEN"
A simple test call is
my-script.sh -t 'test'
However this creates the following output
sedavidw#MacbookPro~$ sh my-script.sh -t 'test'
Token value is
sedavidw#MacbookPro~$
UPDATE
Was able to fix my script by changing it to
#!/bin/bash
while getopts "t:" opt; do
case "${opt}" in
t) JWT_TOKEN="$OPTARG"
;;
\?) echo "Invalid option -$OPTARG" >&2
;;
esac
done
echo "Token value is $JWT_TOKEN"
From the comments it looks like the t: change was helpful for the argument. But still not clear on the syntax change for the "${opt}" part, and would like some clarity there
I'm trying to get a getopts option through a case.
The way my script is setup now, I have a start, stop, status, help options and I want to add some -x options to that to control some variables.
If I don't use the start/stop/status then the -x variables work, but not when I do use them.
Pertinent sections of the code are as follows.
# Variable initialization
DOMAIN="google.ca"
LIMIT="25"
TIMEBETWEEN="10"
INTERATION="1"
PINGS="25"
options='d:l:t:p:h'
while getopts $options opt; do
case $opt in
d) DOMAIN=$OPTARG ;;
l) LIMIT=$OPTARG ;;
t) TIMEBETWEEN=$OPTARG ;;
p) PINGS=$OPTARG ;;
\?) usage ;;
esac
done
shift "$((OPTIND-1))" # Shift off the options and optional --.
and
case "$cmd" in
start)
pingtest_start &
sleep 2
pingtest status
exit 0;
;;
stop)
pingtest_stop
exit 0;
;;
status)
pingtest_status
exit 0;
;;
help)
usage
exit 0;
;;
*)
if [ "$cmd" ]; then
clear
echo "Unknown command '$cmd'"
fi
usage
;;
esac
Other variables work, and if I use the start, then the defaults work.
Edit below:
I found a work around by putting the case for $cmd into a new option and changing the case to $OPTARG
New layout is
options='a:d:l:t:p:h'
while getopts $options opt; do
case $opt in
a) case $OPTARG in
start)
pingtest_start &
sleep 2
pingtest status
exit 0;
;;
stop)
pingtest_stop
exit 0;
;;
status)
pingtest_status
exit 0;
;;
esac
;;
d) DOMAIN=$OPTARG ;;
l) LIMIT=$OPTARG ;;
t) TIMEBETWEEN=$OPTARG ;;
p) PINGS=$OPTARG ;;
\?) usage ;;
esac
done
shift "$((OPTIND-1))" # Shift off the options and optional --.
Do you invoke your script like ./script start -d a -l 42? getopts is expecting the arguments to come first.
– glenn jackman
See glenn jackman's comment. getopts stops as soon as it sees the first non-option argument, so you would need to shift the first argument before you start your getopts loop.
– chepner
I'm having an issue with getopts in a bash script. Basically my script would have to be called with something like:
./myScript /path/to/a/folder -a -b
What I have at the top of my code is this:
while getopts ":ab" opt; do
case $opt in
a)
variable=a
;;
b)
variable=b
;;
\?)
echo "invalid option -$OPTARG"
exit 0
esac
done
echo "$variable was chosen"
Now, this works as long as I call my script without /path/to/a/folder… How can I make it to work with it instead?
Thanks a lot
If you MUST put a path before the arguments, use a shift command to pop the first positional argument off, and leave the rest for getopts.
# Call as ./myScript /path/to/a/folder -a -b
path_argument="$1"
shift # Shifts away one argument by default
while getopts ":ab" opt; do
case $opt in
a)
variable=a
;;
b)
variable=b
;;
\?)
echo "invalid option -$OPTARG"
exit 0
esac
done
echo "$variable was chosen, path argument was $path_argument"
The more-standard answer, as Etan mentioned, is to put the non-option arguments AFTER the options. Prefer this style, as it makes your script more consistent with built-in POSIX option parsing.
# Call as ./myScript -a -b /path/to/a/folder
while getopts ":ab" opt; do
case $opt in
a)
variable=a
;;
b)
variable=b
;;
\?)
echo "invalid option -$OPTARG"
exit 0
esac
done
shift $((OPTIND - 1)) # shifts away every option argument,
# leaving your path as $1, and every
# positional argument as $#
path_argument="$1"
echo "$variable was chosen, path argument was $path_argument"