BASH - getopts not working properly - bash

I'm currently having problems with my script. Basically, what I want to happen is when I execute ./apache_new_vhost.sh -a -d google.com, it will create a file and directories and if I use the -r option, it should delete.
The script was able to use the functions like add_vhost. It could create a configuration and folder however the filename is empty because it could not read the value I passed to $domain.
while getopts ":a:r:d:h" opt; do
case $opt in
a) action=add_vhost
;;
r) action=remove_vhost
;;
d) domain=$OPTARG
;;
h) usage
exit 1
;;
\?) echo "Invalid option: -$OPTARG"
usage
exit 1
;;
:) echo "Error: option -$OPTARG requires an argument."
usage
exit 1
;;
esac
done
#if [ -z $domain ]; then
# usage
# exit 1
if [ $action == "add_vhost" ]; then
echo $action $domain
elif [ $action == "remove_vhost" ]; then
echo $action $domain
fi

The options are processed in the order you specify them on the command line. So in your example, case a) is processed first, and calls your add_vhost function right then.
But the d) case hasn't been processed yet, so you haven't set domain.
You need to change your logic a bit. Rather than calling your functions directly from the case statement, save what action was selected. i.e.:
a) action="add_vhost"
;;
Then after the case, check that you do have an action selected, and call that function.

As per your script you expect argument after option -a. So when you execute your script by
./apache_new_vhost.sh -a -d google.com
then -d will consider as argument given to -a option. So your second argument discarded.To solve it just give any argument after -a (ex: ./apache_new_vhost.sh -a 1 -d google.com )option or make changes in your getopt
while getopts ":ar:d:h" opt; do

Related

Bash menu script with possibility to run with arguments

I have a bash script:
PS3='Please enter your choice: '
options=("1" "2" "3" "4" "Quit")
select opt in "${options[#]}"
do
case $opt in
"1")
echo "Set configuration"
break
;;
"2")
echo "Setting configuration and execution Install"
break
;;
"3")
echo "Setting configuration and execution Unlink"
break
;;
"4")
echo "Running tests"
break
;;
"Quit")
break
;;
*) echo "Selected option '$REPLY' couldn't be find in the list of options";;
esac
done
I have 2 questions:
How can I run this script with predefined option? (For example, I want to execute this script with already selected 1st option)
Is it possible to reuse one option in another option? (For example my 1st option just setting config and my 2nd option also setting the same config and after that execute install, can they be written like if option 2 selected execute 1st option and then 2nd?)
And if something written too badly, I'm open to suggestions =)
How can I run this script with predefined option? (For example, I want
to execute this script with already selected 1st option)
It's a bit ugly with select, move all case logic out from do ... done cycle, make your script take args and rearrange it like this:
#!/bin/bash
PS3='Please enter your choice: '
options=("1" "2" "3" "4" "Quit")
[[ $1 ]] && opt=$1 || select opt in "${options[#]}"; do break; done
case $opt in
"1") echo "Set configuration";;
"2") echo "Setting configuration and execution Install";;
"3") echo "Setting configuration and execution Unlink";;
"4") echo "Running tests";;
"Quit") :;;
*) echo "Selected option '$REPLY' couldn't be find in the list of options";;
esac
Is it possible to reuse one option in another option? (For example my
1st option just setting config and my 2nd option also setting the same
config and after that execute install, can they be written like if
option 2 selected execute 1st option and then 2nd?)
Turn the code in options into functions, this way you could easily reuse it
fun1(){ echo "Set configuration"; }
fun2(){ echo "Execution Install"; }
...
case $opt in
"1") fun1;;
"2") fun1; fun2;;
...
Also there are these operators for case: ;& and ;;&
man bash
...
Using ;& in place of ;; causes execution to continue with the list associated with the next set of patterns.
Using ;;& in place of ;; causes the shell
to test the next pattern list in the statement, if any, and execute any associated list on a successful match.
So if you want to make option 1 also run if option 2 selected this can be done like so:
case $opt in
"2") fun1;&
"1") fun1;;
...
But personally I found this method a bit tricky and hard to read.
If you put the select part in a function
main(){
select opt in "${options[#]}"
do
case $opt in
"1")
set_config # <--- an other funtion for option 1 to reuse it
break
;;
.
.
.
}
# set a default option
def_opt=1
# or use command line argument
def_opt="$1"
you can call main with predefined option '$def_opt' with yes
yes "$def_opt" | main
After digging into this and trying to do my best, I still need a little help to finish my script.
Running script without any parameters are now working perfect.
Passing options in that way (getopts :c:i:u:d:s:h:) leads me to an error message after executing command sh ./script.sh -c => Wrong argument 'c' provided, run sh ./scripts/collection.sh -h for help
Passing options in that way (getopts "ciudsh") => working perfect, but still if I use argument that wasn't passed (ex. x) it would lead to error: Wrong argument '' provided, run sh ./scripts/collection.sh -h for help or sometimes even to this Syntax error: "(" unexpected (expecting "fi")
Please see my full script below, unfortunately for security reasons I can't post the content of the functions itself.
I would appreciate any help on fixing style, errors or anything else.
Based on your advice and other answers on stackoverflow I came up to this:
#!/usr/bin/env bash
#Colors
BRed='\033[1;31m'
Green='\033[0;32m'
BCyan='\033[1;36m'
NC='\033[0m'
f1(){
...
}
f2(){
...
}
f3(){
...
}
f4(){
...
}
f5(){
...
}
Help(){
echo -e "${Green}====================================================================================================================${NC}"
echo "You may execute the commands by selecting a number from menu or pass it as argument, see examples below:"
echo ""
echo -e "${Green}sh $0 ${BCyan}-argument${NC} :To execute specific command"
echo -e "${Green}sh $0 ${NC} :To see menu with all available options"
echo ""
echo -e "${BCyan} -c ${NC}..."
echo -e "${BCyan} -i ${NC}..."
echo -e "${BCyan} -u ${NC}..."
echo -e "${BCyan} -d ${NC}..."
echo -e "${BCyan} -s ${NC}..."
echo -e "${BCyan} -h ${NC}..."
echo -e "${Green}====================================================================================================================${NC}"
exit 1;
}
if [ $# -eq 0 ]
then
PS3='Please enter your choice: '
options=("1" "2" "3" "4" "5" "Help" "Quit")
select opt in "${options[#]}"
do
case $opt in
"1")
f1;;
"2")
f1; f2;;
"3")
f1; f2;;
"4")
f3;;
"5")
f4;;
"Help")
Help;;
"Quit")
break;;
*) echo -e "${BRed}Selected option ${BCyan}'$REPLY'${NC} ${BRed}couldn't be find in the list of provided options${NC}"
break;;
esac
done
fi
while getopts :c:i:u:d:s:h: OPTION
do
case $OPTION in
c)
f1;;
i)
f1; f2;;
u)
f1; f3;;
d)
f4;;
s)
f5;;
h)
Help;;
*) echo -e "${BRed}Wrong argument ${BCyan}'$OPTARG'${NC} ${BRed}provided, run${NC} ${BCyan}sh $0 -h${NC} ${BRed}for help${NC}"
esac
done

How can I check if an empty named parameter has been passed in in bash?

So I want to be able to use a -h option to show the help details. I have:
while getopts ":h:d:n" opt; do
case $opt in
h) help="true" >&2
;;
d) vdir="$OPTARG"
;;
n) vname="$OPTARG"
;;
\?) echo "Error: Invalid option -$OPTARG" >&2
echo "Please use -h for more information"
exit 1
;;
esac
done
# If -h was used, display help and exit
if [ "$help" = "true" ]; then
echo "Help details"
fi
When I pass in details for -d or -n (eg. program -d /var/test/) it receives them fine. However when I do something like program -h, it doesn't work.
I have also tried echoing a line when I do the h) option in the case statement, however, it doesn't get echoed. It seems that when I do -h it doesn't work, I have to send in a value as well (eg program -h "test") and it will do whats required.
If I do something like program -p it shows the error message as is required, -h just does nothing though.
As per comments, the -h does not have a value so should not have a : after it, so that line should be:
while getopts "hd:n:" opt; do
Removing the initial : will give errors. Having no : after the h will mean it does not need a value, while the : after d and n means they need a value.

How to exit recent getopts options?

My script:
run() {
while getopts ":dr" option; do
case "$option" in
d) echo "__DEBUG__";;
r) echo "__RELEASE__";;
esac
done
if [ $option -eq ""]
then
echo "__DEBUG__" ;
fi
}
Hi, i'm using zsh. When i call run without any options
$ run
$ __ DEBUG __
in the first time it's ok and show DEBUG
Then i call with option (d/r) and call run with no agrument again it show this error
$ run -r
$ __ RELEASE __
$ run
$ run-9: parse error: condition expected: r
I don't know how to fix this, it seem that the while loop still has effect
You need to reset OPTIND before the getopts call at the start of your function, this will not be done automatically in this case. In bash, it is set to 1 when a shell or shell script starts, but not when a function is called.
This seems to be what POSIX getopts specifies.
Whenever the shell is invoked, OPTIND shall be initialized to 1.
zsh getopts behaves differently (thanks #PesaThe):
OPTIND has an initial value of 1, and is normally set to 1 upon entry to a shell function and restored upon exit.
Also, when getopts has finished parsing, option will be set to ?, which will not help you determine whether a (valid) option was provided.
Try something like this:
run() {
OPTIND=1
found=0
while getopts ":dr" option; do
case "$option" in
d) echo "__DEBUG__"; found=1 ;;
r) echo "__RELEASE__"; found=1 ;;
esac
done
if [ $found -eq 0 ]
then
echo "__DEBUG__" ;
fi
}
Or a bit simpler (but not identical to yours, to illustrate):
run() {
OPTIND=1
mode="__DEFAULT__"
while getopts ":dr" option; do
case "$option" in
d) mode="__DEBUG__" ;;
r) mode="__RELEASE__" ;;
esac
done
echo "$mode"
}

Bash long options/flags - how to do it?

I am trying to change my working script with getopts to getopt ( long flags ).
Below i present my code which is working.
getopts 'm:' mode
modeValue=$OPTARG
getopts 'p:' parameter
parameterValue=$OPTARG
getopts 'u:' parameter
parameterValue2=$OPTARG
getopts 'l:' parameter
parameterValue3=$OPTARG
getopts 'n:' parameter
parameterValue4=$OPTARG
getopts 'e:' parameter
parameterValue5=$OPTARG
getopts 'w:' parameter
parameterValue6=$OPTARG
getopts 'r:' parameter
parameterValue7=$OPTARG
case $modeValue in
addRepository)
doAddRepository "$parameterValue" "$parameterValue7"
exit $?
;;
addProject)
doAddProject "$parameterValue"
exit $?
;;
addUser)
doAddUser "$parameterValue2" "$parameterValue4" "$parameterValue5" "$parameterValue6"
exit $?
;;
assignProject)
doAssignProject "$parameterValue" "$parameterValue2" "$parameterValue3"
exit $?
;;
*)
#echo "$doShowUsage"
exit 1
;;
esac
Now my script is working like example below:
For add repository: ./script.sh -m addRepository -p NameOfTheProject -r NameOfTheRepository
I want to edit this for something like this:
./script.sh --mode addRepository --project NameOfTheProject --repo NameOfTheRepository
I started to modify code and added something what i present below:
TEMP=`getopt -o m:p:u:l:n:e:c:r: --long mode:,project:,username:,level:,name:,email:,pass:,repo: -n 'test.sh'
-- "$#"` eval set -- "$TEMP"
while true ; do
case "$1" in
-m|--mode)
case "$2" in
addRepository)
doAddRepository=$2 ; shift 2 ;;
addProject)
doAddProject=$2 ; shift 2 ;;
addUser)
doAddUser=$2 ; shift 2 ;;
assignProject)
doAssignProject=$2 ; shift 2 ;;
esac ;;
-h|--help)
case "$2" in
*) echo "$doShowUsage"
exit 1
esac ;;
esac done
My question is : Am I doing it in the right way ? How can I add parameters to the functions "doAddProject/Repository/User...?" Can someone give me some advices? Above functions got different amount of parameters so take a look at it.
Thank you!
Stephane Chazelas wrote a very fine getops-long shell script that I use in my bash debugger. You can copy that script and use it.
If you run that program setting variable test_getopts_long, e.g.
test_getopts_long=1 bash getopts_long.sh
you'll see extensive examples for how to use, and it tests itself.

Execution sequence in shell script and passing the right arguments to functions

I have a shell script that I use to launch some ROS launcher file (not that important for my question). My requirement is to use some arguments and therefore I am using getopts.
Basically, I am getting one file or a few, playing them with ROS and simultaneously recording all the files back to a single file. After that, if I have provided an argument -r or -c, I would like to run two additional operations (reindex and compress) on the recorded file. But it is required that the other process are done, before I can run the -r and -c. I am using the wait keyword, but I am not sure I really understand the flow. In other words, the playing and recording should be done and only then -r and -c should be run if provided as arguments.
Second question is related to how do I get or pass the same file that was outputted to these two functions (reindex and compress)?
So my desired format is:
./myscript -i file1 -i file2 -o /home/output -r -c
#!/bin/bash
OUTPUT_FILE_NAME="output_$(date +%Y.%m.%d--%H_%M_%S)"
usage="
$(basename "$0") [-i] [-o] [-r] [-c] [-h] -- to be done"
# Reset is necessary if getopts was used previously in the script.
OPTIND=1
while getopts ":i:orch:" opt; do
case $opt in
i ) echo "INPUT file - argument = $OPTARG"; input_files+=($OPTARG) ;;
o ) echo "OUTPUT dir - argument = $OPTARG"; output_dir=($OPTARG) ;;
r ) echo "REINDEX"; reindex ;;
c ) echo "COMPRESS"; compress ;;
h ) echo "$usage"
graceful_exit ;;
* ) echo "$usage"
exit 1
esac
done
# Shift off the options
shift $((OPTIND-1))
roslaunch myLauncher.launch &
echo "Number of loaded files: ${#input_files[#]}"
echo -n "FILES are:"
rosbag play ${input_files[#]} &
rosbag record -o $output_dir/$OUTPUT_FILE_NAME -a &
wait
function reindex{
rosbag reindex $output_dir/$OUTPUT_FILE_NAME
}
function compress{
rosbag reindex $output_dir/$OUTPUT_FILE_NAME
}
Thank you in advance!
You're very close to where you need to be — and using getopts puts you firmly on the correct track, too. Note whether or not you need to reindex or compress in the option parsing loop. Then, after the music has been played and the output file written, run the code from the functions if you need to:
#!/bin/bash
OUTPUT_FILE_NAME="output_$(date +%Y.%m.%d--%H_%M_%S)"
usage="$(basename "$0") [-i file] [-o file] [-r] [-c] [-h]"
input_files=() # Empty list of files/tracks
output_dir="." # Default output directory
r_flag="no" # No reindex by default
c_flag="no" # No compress by default
while getopts ":i:orch:" opt; do
case $opt in
(i) echo "INPUT file - argument = $OPTARG"; input_files+=("$OPTARG");;
(o) echo "OUTPUT dir - argument = $OPTARG"; output_dir="$OPTARG";;
(r) echo "REINDEX"; r_flag="yes";;
(c) echo "COMPRESS"; c_flag="yes";;
(h) echo "$usage" >&2; exit 0;;
(*) echo "$usage" >&2; exit 1;;
esac
done
# Shift off the options
shift $((OPTIND-1))
if [ $# != 0 ] || [ "${#input_files[#]}" = 0 ] ||
then
echo "$usage" >&2
exit 1
fi
roslaunch myLauncher.launch &
echo "Number of loaded files: ${#input_files[#]}"
echo -n "FILES are:"
rosbag play "${input_files[#]}" &
rosbag record -o "$output_dir/$OUTPUT_FILE_NAME" -a &
wait
if [ "$r_flag" = "yes" ]
then
rosbag reindex "$output_dir/$OUTPUT_FILE_NAME"
fi
if [ "$c_flag" = "yes" ]
then
rosbag compress "$output_dir/$OUTPUT_FILE_NAME"
fi
I didn't keep the functions since they didn't provide any value in the rewritten code.

Resources