is bash getopts function destructive to the command-line options? - bash

Can you use the bash "getopts" function twice in the same script?
I have a set of options that would mean different things depending on the value of a specific option. Since I can't guarantee that getopts will evaluate that specific option first, I would like to run getopts one time, using only that specific option, then run it a second time using the other options.

Yes, just reset OPTIND afterwards.
#!/bin/bash
set -- -1
while getopts 1 opt; do
case "${opt}" in
1) echo "Worked!";;
*) exit 1;
esac
done
OPTIND=1
set -- -2
while getopts 2 opt; do
case "${opt}" in
2) echo "Worked!";;
*) exit 1;
esac
done

getopts does not modify the original arguments, as opposed to the older getopt standalone executable. You can use the bash built-in getopts over and over without modifying your original input.
See the bash man page for more info.
HTH.
cheers,
Rob

Related

Shell: two loops over coomandline parameters

In a shell-script I have a loop over the positional parameters using the shift-command. After the loop I d like to reset and start another loop over the parameters. Is it possible to go back to start?
while [ $# -gt 0 ]; do
case "$1" in
"--bla")
doing sth
shift 2
;;
*)
shift 1
;;
esac
done
You can save arguments in a temporary array. Then restore positional arguments from it.
args=("$#") # save
while .....
done
set -- "${args[#]}" # restore
Don't use shift if you need to process the arguments twice. Use a for loop, twice:
for arg in "$#"
do
…
done
If you need to process argument options, consider using the GNU version of getopt (rather than the Bash built-in getopts because that only handles short options). See Using getopts in bash shell script to get long and short command line options for many details on how to do that.

How to use getopts in bash script? [duplicate]

This question already has answers here:
Optional option argument with getopts
(15 answers)
Closed 5 years ago.
I'm trying to use getopts like this:
#!/bin/bash
while getopts "i" option
do
case "${option}"
in
i) INT=${OPTARG};;
esac
done
echo "$INT"
But it prints $INT only if i use getopts "i:". If I understand correctly, the colons in the optstring mean that values are required for the corresponding flags. But I want to make this flag optional.
Can anyone explain why the script acts like this and how can I fix it?
You cannot make it (bash getopts) optional like that. The "getopts" does not support mandatory or optional options.
You would need to code for that.
And if a ":" is specified then there needs to be an argument to that option. There is no way to around it.
The following code snippets shows how to check for mandatory arguments.
# Mandatory options
arg1=false;
..
...
case "${option}"
in
i) INT=${OPTARG}; arg1=true;
;;
esac
if ! $arg1;
then
echo -e "Mandatory arguments missing";
# assuming usage is defined
echo -e ${usage};
exit 1;
fi

Using getopts with a switchless path

I am trying to write a short script, utilizing getopts. I want it to take optional switches, or just run as the default. I have a -d switch to enable debugging, and I'd like every other argument to be a path. The ideal command line looks as such, with paths being optional, and theoretically limitless:
$0 [-d] [/path1[ /path2[ ...]]]
I am currently using getopts as such below:
while getopts ":d" opt; do
case $opt in
d)
DEBUG=true
;;
h)
echo USAGE: $0 \[-d\] \[\/mount\/point\/1 ...\]
exit 0
;;
\?)
echo Incorrect syntax
;;
esac
done
What can I put in the while getopts section, and in the case set, to allow paths to be entered, as many as needed?
You don't need anything in the loop or getopts call for that. getopts stops at the first non-option.
After your loop all your paths will still be in positional arguments available for use.
Also you don't have h in your getopts string so it isn't valid.

Bug in parsing args with getopts in bash

I was trying to modify the bd script to use getopts. I am a newbie at bash scripting
my script is
while getopts ":hvis:d:" opt
do
...
done
...
echo $somedirpath
cd "$somedirpath"
this runs fine when doing
$ ./bd -v -i -s search
or
$ ./bd -is search -d dir
But when running it like this
$ . ./bd -s search
getopts doesn't read the arguments at all. And all the variables I set in the while loop according to the arguments are all not set, so the script no longer works. Please help!
Setting OPTIND=1 before invoking getopts works fine.
The problem is that getopts relies on OPTIND to loop through the arguments provided, and after sourcing the script, it will be set to some value greater than 1 by getopts according to how many arguments you pass. This value gets carried over even after the script ends(because its being sourced). So the next time its sourced, getopts will pick up from that OPTIND, rather than starting from 1!
This might cause strange behaviour with other scripts, and I don't know how safe this is. But it works!
For a better workaround, I think what #tripleee suggests looks safe and robust.
When you source a script, the arguments parsed by getopts are those of the current shell, not the parameters on the source command line.
The common workaround is to have your script merely print the path, and invoke it like cd "$(bd)" instead (perhaps indirectly through a function or alias).
Setting OPTIND=1 may not work reliably on zsh. Try to use something different than getopts:
while [ "$#" -gt 0 ]
do
case "$1" in
-h|--help)
help
return 0
;;
-o|--option)
option
return 0
;;
-*)
echo "Invalid option '$1'. Use -h|--help to see the valid options" >&2
return 1
;;
*)
echo "Invalid option '$1'. Use -h|--help to see the valid options" >&2
return 1
;;
esac
shift
done

How to have a positional argument before options in a shell script?

I want to have a shell script that takes a file name as first positional argument followed by options (./test.sh <file> [options]). However, getopts doesn't work when I give a positional argument before options. I have the following code:
while getopts "h" opt; do
case $opt in
h)
echo usage
;;
;;
esac
done
echo $1
./test.sh -h prints usage on the shell, but ./test.sh test -h prints test on the shell. So when I give a positional argument before an option it's not doing anything with the option. It does work when having the positional arguments after the option (change echo $1 to echo $BASH_ARGV and the call to ./test.sh -h test). How can I have the positional argument before the options?
The shell builtin getopts does not support reordering the parameters. If you want parameter reordering, you need to use one of the enhanced getopt variants (e.g. gnu getopt or bsd getopt). Please note that the default bsd getopt does not support long options (e.g. when used on Mac OS X)
Try to replace "h" to h
while getopts h opt; do...
And also you have to add minus in your case
case "$opt" in
-h)...
If you know, that your [file] will always be present, can you just use
filename="$1"
shift
And than parse other arguments

Resources