I would like to make one of the optional characters (-t) which should not accept any argument in getopts bash. This is where i got so far
while getopts ":hb:q:o:v:t" opt; do
case $opt in
b)
Blasting_list=$OPTARG
;;
l)
query_lincRNA=$OPTARG
;;
q)
query_species=$OPTARG
;;
o)
output=$OPTARG # Output file
;;
t)
species_tree=$OPTARG
;;
h)
usage
exit 1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
I want to run the above script like this..
bash test.sh -b Blasting_list.txt -l Sample_query.fasta -q Atha -o test_out -v 1e-20 -t
Then it should execute the below loop
(-----)
if [ ! -z $species_tree ];
then
mkdir -p ../RAxML_families
perl /Batch_RAxML.pl aligned_list.txt
rm aligned_list.txt
else
rm aligned_list.txt
fi
(-----)
And if i run like this, it should skip the loop.
bash test.sh -b Blasting_list.txt -l Sample_query.fasta -q Atha -o test_out -v 1e-20
(-----)
(-----)
I tried to play with getopts options but i cannot make it work.
probably the easiest way is to set species_tree to true iff there's the -t command line flag:
species_tree=false # <-- addition here
while getopts ":hb:q:o:v:t" opt; do
case $opt in
...
t)
species_tree=true # <-- change here
;;
...
esac
done
if $species_tree; then # <-- change here
...
Related
I would like to build a script with getopts, that continues in the flag, when an $OPTARG isn't set.
My script looks like this:
OPTIONS=':dBhmtb:P:'
while getopts $OPTIONS OPTION
do
case "$OPTION" in
m ) echo "m"
t ) echo "t"
d ) echo "d";;
h ) echo "h";;
B ) echo "b";;
r ) echo "r";;
b ) echo "b"
P ) echo hi;;
#continue here
\? ) echo "?";;
:) echo "test -$OPTARG requieres an argument" >&2
esac
done
My aim is to continue at my comment, when there is no $OPTARG set for -P.
All I get after running ./test -P is :
test -P requieres an argument
and then it continues after the loop but I want to continue in the -P flag.
All clear?
Any Ideas?
First, fix the missing ;; in some of the case branches.
I don't think you can: you told getopts that -P requires an argument: two error cases
-P without an argument is the last option. In this case getops sees that nothing follows -P and sets the OPTION variable to :, which you handle in the case statement.
-P is followed by another option: getopts will simply take the next word, even if the next word is another option, as OPTARG.
Change the case branch to
P ) echo "P: '$OPTARG'";;
Then:
invoking the script like bash script.sh -P -m -t, the output is
P: '-m'
t
invoking the script like bash script.sh -Pmt, the output is
P: 'mt'
This is clearly difficult to work around. How do you know if the user intended the option argument to be literally "mt" and not the options -m and -t?
You might be able to work around this using getopt (see the canonical example) using an optional argument for a long option (those require an equal sign like --long=value) so it's maybe easier to check if the option argument is missing or not.
Translating getopts parsing to getopt -- it's more verbose, but you have finer-grained control
die() { echo "$*" >&2; exit 1; }
tmpArgs=$(getopt -o 'dBhmt' \
--long 'b::,P::' \
-n "$(basename "$0")" \
-- "$#"
)
(( $? == 0 )) || die 'Problem parsing options'
eval set -- "$tmpArgs"
while true; do
case "$1" in
-d) echo d; shift ;;
-B) echo B; shift ;;
-h) echo h; shift ;;
-m) echo m; shift ;;
-t) echo t; shift ;;
--P) case "$2" in
'') echo "P with no argument" ;;
*) echo "P: $2" ;;
esac
shift 2
;;
--b) case "$2" in
'') echo "b with no argument" ;;
*) echo "b: $2" ;;
esac
shift 2
;;
--) shift; break ;;
*) printf "> %q\n" "$#"
die 'getopt internal error: $*' ;;
esac
done
echo "Remaining arguments:"
for ((i=1; i<=$#; i++)); do
echo "$i: ${!i}"
done
Successfully invoking the program with --P:
$ ./myscript.sh --P -mt foo bar
P with no argument
m
t
Remaining arguments:
1: foo
2: bar
$ ./myscript.sh --P=arg -mt foo bar
P: arg
m
t
Remaining arguments:
1: foo
2: bar
This does impose higher overhead on your users, because -P (with one dash) is invalid, and the argument must be given with =
$ ./myscript.sh --P arg -mt foo bar
P with no argument
m
t
Remaining arguments:
1: arg
2: foo
3: bar
$ ./myscript.sh --Parg mt foo bar
myscript.sh: unrecognized option `--Parg'
Problem parsing options
$ ./myscript.sh -P -mt foo bar
myscript.sh: invalid option -- P
Problem parsing options
$ ./myscript.sh -P=arg -mt foo bar
myscript.sh: invalid option -- P
myscript.sh: invalid option -- =
myscript.sh: invalid option -- a
myscript.sh: invalid option -- r
myscript.sh: invalid option -- g
Problem parsing options
Do not mix logic with arguments parsing.
Prefer lower case variables.
My aim is to continue at my comment, when there is no $OPTARG set for -P
I advise not to. The less you do at one scope, the less you have to think about. Split parsing options and executing actions in separate stages. I advise to:
# set default values for options
do_something_related_to_P=false
recursive=false
tree_output=false
# parse arguments
while getopts ':dBhmtb:P:' option; do
case "$option" in
t) tree_output=true; ;;
r) recursive="$OPTARG"; ;;
P) do_something_related_to_P="$OPTARG"; ;;
\?) echo "?";;
:) echo "test -$OPTARG requieres an argument" >&2
esac
done
# application logic
if "$do_something_related_to_P"; then
do something related to P
if "$recursive"; then
do it in recursive style
fi
fi |
if "$tree_output"; then
output_as_tree
else
cat
fi
Example of "don't put programming application logic in the case branches" -- the touch command can take a -t timespec option or a -r referenceFile option but not both:
$ touch -t 202010100000 -r file1 file2
touch: cannot specify times from more than one source
Try 'touch --help' for more information.
I would implement that like (ignoring other options):
while getopts t:r: opt; do
case $opt in
t) timeSpec=$OPTARG ;;
r) refFile=$OPTARG ;;
esac
done
shift $((OPTIND-1))
if [[ -n $timeSpec && -n $refFile ]]; then
echo "touch: cannot specify times from more than one source" >&2
exit 1
fi
I would not do this:
while getopts t:r: opt; do
case $opt in
t) if [[ -n $refFile ]]; then
echo "touch: cannot specify times from more than one source" >&2
exit 1
fi
timeSpec=$OPTARG ;;
r) if [[ -n $timeSpec ]]; then
echo "touch: cannot specify times from more than one source" >&2
exit 1
fi
refFile=$OPTARG ;;
esac
done
You can see if the logic gets more complicated (as I mentioned, exactly one of -a or -b or -c), that the case statement size can easily balloon unmaintainably.
I have a shell script that takes parameters, below is the code..
Right now it will only accept parameters if passed if called like this: script.sh --mode=load (or -m=load)
Is there a way to modify this so that it can be called with or without the "=" sign, so that I can call: script.sh --mode load (or -m load)
Ideally needs to work in pure bash as I don't have access to install additional tools, etc.
for i in "$#"
do
case $i in
-m=*|--mode=*)
MODE="${i#*=}"
if [[ $MODE =~ ^(dump|load)$ ]]; then
echo "" > /dev/null
else
bark "Invalid --mode set, set this to dump or load.";
exit 1
fi
;;
-p=*|--db-path=*)
DBPATH="${i#*=}"
;;
-d=*|--dump-dir=*)
DUMPDIR="${i#*=}"
;;
-l=*|--list-file=*)
TABLES="${i#*=}"
# check if file exists on disk
if [ -e $TABLES ]
then
echo "" >> /dev/null
else
bark "Table file not found!";
exit 1
fi
;;
-t=*|--tenant-name=*)
TENANT="${i#*=}"
# check if tenant is correct
if [[ $TENANT =~ ^($TENANT_LIST)$ ]]; then
echo "" >> /dev/null
else
bark "Tenant name does not match, aborting.";
exit 1
fi
;;
-s|--shared)
SHARED=YES
;;
*) usage # unknown option
;;
esac
done
My bash version:
bash --version
GNU bash, version 4.3.22(1)-release (powerpc-ibm-aix5.1.0.0)
Loop on $#. When $1 is "-m", do a shift. So in the next loop $1 will now be the argument to the -m option.
script.sh --mode load
# FIRST LOOP
$# is "--mode load"
$1 is "--mode"
shift
# SECOND LOOP
$# is "load"
$1 is "load"
This is also useful if you can specify many arguments instead of just one like you have right now. Error checking should be done to validate your argument values, and if a user did script.sh --mode with no other argument.
Don't reinvent the wheel.
If you're OK with just 1 character options, use the bash builtin getopts
#!/bin/bash
while getopts :m:p:d:l:t:s opt; do
case $opt in
m) mode=$OPTARG ;;
p) dbpath=$OPTARG ;;
d) dumpdir=$OPTARG ;;
l) tables=$OPTARG
# test file existence
;;
t) tenant=$OPTARG
# test tenant
;;
s) shared=YES ;;
:) echo "Missing argument for option -$OPTARG" >&2
exit 2
;;
*) echo "Invalid option -$OPTARG" >&2
exit 2
;;
esac
done
shift $((OPTIND - 1))
cat << SHOW_VARS
I have:
mode=$mode
dbpath=$dbpath
dumpdir=$dumpdir
tables=$tables
tenant=$tenant
shared=$shared
rest of args=$*
SHOW_VARS
Otherwise, you may be able to use the external getopt program to help parse your args. I don't have an AIX box to test on, so YMMV
tempargs=$(
getopt \
-o m:d:l:t:s \
--long mode:,db-path:,dump-dir:,list-file:,tenant-name:,shared \
-- "$#"
)
if [[ $? -ne 0 ]]; then echo "Error..." >&2; exit 2; fi
eval set -- "$tempargs"
while true; do
case $1 in
-m|--mode) mode=$2; shift 2;;
-p|--db-path) dbpath=$2; shift 2;;
-d|--dump-dir) dumpdir=$2; shift 2;;
-l|--list-file) tables=$2
# test file existence
shift 2
;;
-t|--tenant-name) tenant=$2
# test tenant
shift 2
;;
-s|--shared) shared=YES; shift;;
--) shift; break ;;
*) echo "Error..." >&2; exit 2 ;;
esac
done
I have a bash script which takes few command line args and a filename as inline parameter. I am not able to read the inline parameter.
sh test.sh -a a -b b -c c < pwd.txt
test.sh has
if [ $# = 0 ]
then
echo $USAGE >&2
exit $STATUS_ERROR_FAIL
fi
# Parse command line options.
while getopts a:b:c: OPT;
do
case "$OPT" in
a)
a="$OPTARG"
;;
b)
b="$OPTARG"
;;
c)
c="$OPTARG"
;;
\?)
# getopts issues an error message
echo $USAGE
exit $STATUS_ERROR_FAIL
;;
esac
done
shift $((OPTIND-1))
echo "1=$1"
your script is working fine, your error is
echo "1=$1"
if you want to see your parameter you should add an echo/print in your case
#!/bin/bash
if [ $# = 0 ]
then
echo $USAGE >&2
exit $STATUS_ERROR_FAIL
fi
# Parse command line options.
while getopts a:b:c: OPT;
do
case "$OPT" in
a)
a="${OPTARG}"
echo "a[$a]"
;;
b)
b="${OPTARG}"
echo "b[$b]"
;;
c)
c="${OPTARG}"
echo "c[$c]"
;;
\?)
# getopts issues an error message
echo $USAGE
exit $STATUS_ERROR_FAIL
;;
esac
done
shift $((OPTIND-1))
or
you can add the echo/print at the end of the script.. it's depend by your needs
output
[shell] ➤ ./t -a 1 -b 2 -c 3
a[1]
b[2]
c[3]
Regards
Claudio
while getopts ":hufc:p:i" opt; do
case $opt in
h)
usage
exit 1
;;
u)
DOUPDATE=false
;;
f)
DOCONFIRMATION=false
;;
c)
CUSTOMERTYPE=$OPTARG
;;
p)
CUSTOMERPROFILE=$OPTARG
;;
i)
echo "LOL $INSTALL"
INSTALL=true
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
Above is my bash code. When I try to use "i" as a parameter in the command line, case i is not entered.
What should I do?
If your command line has any invalid options prior to the i, it won't parse any farther than the error. If your command line has a -c or -p without a subsequent argument to go into CustomerType or CustomerProfile, it will not parse any farther.
#Examples that should work
script.sh -i
script.sh -c customerX -p profileX -i
script.sh -iz
#Examples that will not work
script.sh -hi #-h option will exit before it parses -i
script.sh -zi #invalid option z will exit
script.sh -c customerX -p -i #Missing required argument after -p
I want to call a bash script like this
$ ./scriptName -o -p -t something path/to/file
This is as far as I get
#!/bin/bash
o=false
p=false
while getopts ":opt:" options
do
case $options in
o ) opt1=true
;;
p ) opt2=true
;;
t ) opt3=$OPTARG
;;
esac
done
but how do I get the path/to/file?
You can do something like:
shift $(($OPTIND - 1))
first_arg=$1
second_arg=$2
after the loop has run.
To capture all the remaining parameters after the getopts processing, a good solution is to shift (remove) all the processed parameters (variable $OPTIND) and assign the remaining parameters ($#) to a specific variable.
Short answer:
shift $(($OPTIND - 1))
remaining_args="$#"
Long example:
#!/bin/bash
verbose=false
function usage () {
cat <<EOUSAGE
$(basename $0) hvr:e:
show usage
EOUSAGE
}
while getopts :hvr:e: opt
do
case $opt in
v)
verbose=true
;;
e)
option_e="$OPTARG"
;;
r)
option_r="$option_r $OPTARG"
;;
h)
usage
exit 1
;;
*)
echo "Invalid option: -$OPTARG" >&2
usage
exit 2
;;
esac
done
echo "Verbose is $verbose"
echo "option_e is \"$option_e\""
echo "option_r is \"$option_r\""
echo "\$# pre shift is \"$#\""
shift $((OPTIND - 1))
echo "\$# post shift is \"$#\""
This will output
$ ./test-getopts.sh -r foo1 -v -e bla -r foo2 remain1 remain2
Verbose is true
option_e is "bla"
option_r is " foo1 foo2"
$# pre shift is "-r foo1 -v -e bla -r foo2 remain1 remain2"
$# post shift is "remain1 remain2"