I would like to run a shell script that will print either 'yesterday' or 'tomorrow' based on the option that is provided at the command line. If the option is -y, then the output should be 'yesterday', otherwise is 'tomorrow'. In addition I would like to add the option help -h which will print the syntax of the script.
I made the script as:
#! /bin/bash
h= y=
while getopt :f:vql opt
do
case $opt in
y) setday=true
;;
h) tohelp=true
;;
esac
done
shift $((OPTIND - 1))
if [setday=true]
NAME=$yesterday
else
NAME=$tomorrow
fi
if [tohelp=true]
MSG=$'runner [-y]'
echo $NAME
echo $MSG
but when I run it, I simply get an infinite loop that prints
-- opt
-- opt
-- opt
etc
What I am getting wrong?
This should work.
#!/bin/bash
setday=false
tohelp=false
yesterday="Yesterday"
tomorrow="Tomorrow"
for i in "$#"
do
case $i in
-y)
setday=true
shift
;;
-h)
tohelp=true
shift
;;
esac
shift
done
if [ $setday = true ]; then
NAME=$yesterday
else
NAME=$tomorrow
fi
if [ $tohelp = true ]; then
MSG='runner [-y]'
fi
echo $NAME
echo $MSG
#itachi's answer looks good. Although, if you want to continue using getopts, this should also work:
#! /bin/bash
yesterday="yesterday"
tomorrow="tomorrow"
setday=false
tohelp=false
while getopts "yh" opt
do
case $opt in
y)
setday=true
;;
h)
tohelp=true
;;
esac
done
shift $((OPTIND - 1))
if $tohelp; then
echo "runner [-y]"
exit
elif $setday; then
NAME=$yesterday
else
NAME=$tomorrow
fi
echo $NAME
Output:
$ ./test.sh
tomorrow
$ ./test.sh -y
yesterday
$ ./test.sh -h
runner [-y]
Related
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
usage ()
{
echo "USAGE MANUAL"
echo "-u -> displays this usage manual"
echo "-t -> sets EXEC variable to echo"
echo "-t -> sets DEPTH variable to inputed string"
exit 1
}
while getopts ":t:u:d:" o; do
case "${o}" in
t)
EXEC=echo
;;
d)
DEPTH=${OPTARG}
;;
u)
usage
;;
esac
done
shift $((OPTIND-1))
if [ -z "${d}" ] || [ -z "${t}" ]; then
usage
fi
If i get it right if i do this:
./script -d
I have to provide something like:
./script -d full
The question is, how do i restrict what the argument of d can be. Like if someone types ./script -d full or ./script -d empty its gonna change the value of DEPTH to full or empty, but if they put something like ./script -d infinite the script doesnt run and echo somthing like wrong argument provided.
You can use a nested case:
d) case "$OPTARG" in
full|empty) DEPTH=$OPTARG ;;
*) echo 'Invalid depth' >&2 ; exit 1 ;;
esac
;;
I just wrote a script in bash, which work expect for multi long option:
#!/bin/bash
OPTS=`getopt -q -o fdhl: -l free,df,help,log: -- "$*"`
#Check if error with getopt
if [ $? != 0 ]
then
echo -e "error: parameter could not be found\n\nUsage:\n supervision [options]\n\n Try 'supervision --help'\n or 'supervision -h'\n for additional help text." ;
exit 1
fi
eval set -- "$OPTS"
while true ; do
case "$1" in
-f|--free)
free -h ;
shift;;
-d|--df)
df -h ; #Run df system command
shift;;
-l|--log)
case "$2" in
"") echo "miss file" ;
shift 2;; #No file passed as parameter
*)
df -h >> "$2" ;
shift 2;;
esac ;;
-h|--help) #Display help
shift;;
--) #End of parsed parameters list
shift ; break ;;
*)
break ;;
esac
done
I don't get why i'm supposed to, when I use more than 1 long option, for example:
sh myscript --free --df
And when I use --log:
sh myscript --log logfile
Both case exit on the if [ $? != 0 ], seems like the element which follow the 1st long option doesn't get parsed.
Ok, I figured out and it's all due to the using of "$*" instead of "$#" in the getopt call. I don't exactly why, i guessed both do the same thing, but it turns out to be the one which causes the problem.
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.
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`