pass verbatim arguments to getopts without $# or $* inside posix shell script - shell

I'm trying to pass an argument as "verbatim" to my script, the problem is that the argument in inside double quotes "" and it starts with a double dash/double hyphen --.
This is an example
script.sh -f "--conf=bla"
In my script both $* and $# transform this into
-f --conf=bla
and when this thing reaches getopts there is no way to decode this information the way it should be.
And by the way this is my getopts
foo()
{
while getopts ":f:" vars
do
case ${vars} in
f ) MYVAR=${OPTARG};;
* ) Err; exit 1;;
esac
done
shift $((OPTIND-1))
}
I would like to store --conf=bla inside MYVAR, I can't find a way to do this, apparently I can't control the way the double quotes are stripped away and, in general, I can't really pass text as verbatim to my script.
How I can control this ?

$ cat t.sh
#!/bin/sh
foo()
{
# unset OPTIND
while getopts ":f:" vars
do
case ${vars} in
f) MYVAR=${OPTARG} ;;
*) echo "error"; exit 1 ;;
esac
done
shift $((OPTIND-1))
echo "\$MYVAR=[${MYVAR}]"
}
foo "$#"
.
$ ./t.sh -f "--conf=blah"
$MYVAR=[--conf=blah]
Please elaborate?

Related

Bash: Extract remaining unflagged arguments (when using getopts)

I'm using the getopts command to process the flags in my bash script, using the structure found in this answer: https://stackoverflow.com/a/21128172/2230446
#!/bin/bash
t_flag='30'
print_usage() {
printf "Usage: ..."
}
while getopts 't:' flag; do
case "${flag}" in
t) t_flag="${OPTARG}" ;;
*) print_usage
exit 1 ;;
esac
done
echo "${t_flag}"
I run the script with the following command:
./test.sh -t dog cat fish
My goal is to extract cat fish to a variable so I can use it in the rest of the script, similar to how the t_flag was extracted to a variable.
Discard -t dog after parsing it, e.g:
shift $((OPTIND-1))
Then only cat and fish will be left as positional parameters. To extract them to a variable:
var=$*

Parsing unique string into a variable in the posix sh shell

I have this weird behaviour that I can't understand; at some point I need to pass some flags to my script, one flag in particular is supposed to carry out a series of options used inside my script, for example I'm invoking my script as
sh script.sh --flag1="-options1=value1 -options2=value2" --flag2
the result is
-options1=value1
-options2=value2
1
,and so flag1 magically appears as a multiline declaration and something happens and I don't really get the logic behind this behaviour.
This is the complete script
parse()
{
while [ $# -gt 0 ]
do
case "$1" in
--flag1=* ) FLAG_1="${1#*=}"; shift;;
--flag2 ) FLAG_2="1"; shift;;
(*) printf $0' : error - unrecognized option '$1'\n' 1>&2; exit 1;;
esac
done
}
printvar()
{
printf %s'\n' $FLAG_1
printf %s'\n' $FLAG_2
}
parse "$#"
printvar
What I'm doing wrong here ?
To get FLAG_1 as a single line, just quote the variable:
printf "%s\n" "$FLAG_1"
The below snippet should clarify:
$ printf "%s\n" ab cd
ab
cd
$ printf "%s\n" "ab cd"
ab cd

Having getopts in a seperate function in a bash script

I have written the following shell script to parse arguments and print. Its not working as intended. I am sure that I am not passing correct argument to the function thats evaluating the optional params. Can some one please help is correcting it
#! /bin/sh
MAX_NO_OF_DATABASE=500;
MAX_NO_OF_CONCURRENT_REQUEST=500;
MAX_NO_OF_REQUEST=500;
function showUsage(){
echo "Sample Usage : ./benchmarking.sh <CORE_URL> <SYNC_SERVER_URL> [-d MAX_NO_OF_DATABASE] [-c MAX_NO_OF_CONCURRENT_REQUEST] [-n MAX_NO_OF_REQUEST]";
exit;
}
function readArguements(){
# Check for core url
if [ -z $1 ]; then
echo "CORE_URL is not specified"
showUsage
fi
# Check for sync server url
if [ -z $2 ]; then
echo "SYNC_SERVER_URL is not specified"
showUsage
fi
}
function readOptionalArguements(){
# Check for the optional parameters
while getopts dcn: opt
do
case $opt in
d) MAX_NO_OF_DATABASE="$OPTARG";;
c) MAX_NO_OF_CONCURRENT_REQUEST="$OPTARG";;
n) MAX_NO_OF_REQUEST="$OPTARG";;
esac
done
}
readArguements $*
readOptionalArguements $*
echo "$1 $2 $MAX_NO_OF_DATABASE $MAX_NO_OF_CONCURRENT_REQUEST $MAX_NO_OF_REQUEST"
When I run it ./benchmarker.sh core_url sync_url -d 500 -c 100 -n 200
It prints as
core_url sync_url 500 500 500
I had debugging on and I could see that it does not evaluate the switch block. Am I passing the correct arguments to readOptionalArguements
You should set the string to d:c:n:, because all three options take parameters.
Read carefully what man bash says about getopts:
When the end of options is encountered, getopts exits with a return
value greater than zero. OPTIND is set to the index of the first non-
option argument, and name is set to ?.
Therefore, you have to process the first two non-option parameters before processing the options.
core_url=$1
sync_server=$2
shift 2
readArguements "$#"
readOptionalArguements "$#"
Also, if you are using bash, do not write #!/bin/sh in the shebang line.
getopts stops on the first non-option argument, so you need to shift-out those non-option arguments before using them in getopts.
So for example:
function readOptionalArguements(){
# skip two mandatory arguments
shift 2
# Check for the optional parameters
while getopts d:c:n: opt
do
case $opt in
d) MAX_NO_OF_DATABASE="$OPTARG";;
c) MAX_NO_OF_CONCURRENT_REQUEST="$OPTARG";;
n) MAX_NO_OF_REQUEST="$OPTARG";;
esac
done
}
Also, you should declare a local OPTIND to keep that variable safe in case you call the function multiple times.
I believe, the problem may be in the readOptionalArguments method (btw, this is misspelled in your question). The following seems to work for me (in bash)
function readOptionalArguements() {
# Check for the optional parameters
while getopt "d:c:n:" $*
do
echo "opt: <$1>";
case $1 in
-d) shift; MAX_NO_OF_DATABASE="$1"; shift;;
-c) shift; MAX_NO_OF_CONCURRENT_REQUEST="$1"; shift;;
-n) shift; MAX_NO_OF_REQUEST="$1"; shift;;
--) break;;
*) break;;
esac
done
}

getopts printing help when no cmd. line argument was matched

I'm trying to use getopts in bash to parse command line arguments, but I couldn't figure out how to implement "default" action, if no argument was matched (or no cmdline argument given).
This is silghtly simplified version of what I've tried so far:
while getopts v:t:r:s:c: name;
do
case $name in
v) VALIDATE=1;;
t) TEST=1;;
r) REPORT=1;;
s) SYNC=1;;
c) CLEAR=1;;
*) print_help; exit 2;;
\?) print_help; exit 2;;
esac
done
Is there any (simple) way to make it call print_help; exit 2; on non matching input?
Looking between your question and the comments on Aditya's answer, I'd recommend the following:
[getopts]$ cat go
#!/bin/bash
function print_help { echo "Usage" >&2 ; }
while getopts vtrsc name; do
case $name in
v) VALIDATE=1;;
t) TEST=1;;
r) REPORT=1;;
s) SYNC=1;;
c) CLEAR=1;;
?) print_help; exit 2;;
esac
done
echo "OPTIND: $OPTIND"
echo ${##}
shift $((OPTIND - 1))
while (( "$#" )); do
if [[ $1 == -* ]] ; then
echo "All opts up front, please." >&2 ; print_help ; exit 2
fi
echo $1
shift
done
Since each of those are boolean flag options, you don't need (and in fact, do not want) the arguments, so we get rid of the colons. None of those characters are in IFS, so we don't need to wrap that in quotes, it will be one token for getopts anyway.
Next, we change the \? to a single ? and get rid of the *, as the * would match before the literal \?, and we might as well combine the rules into a single default match. This is a good thing, since any option specified with a - prefix should be an option, and users will expect the program to fail if they specify an option you don't expect.
getopts will parse up to the first thing that isn't an argument, and set OPTIND to that position's value. In this case, we'll shift OPTIND - 1 (since opts are 0-indexed) off the front. We'll then loop through those args by shifting them off, echoing them or failing if they start with a -.
And to test:
[getopts]$ ./go
OPTIND: 1
0
[getopts]$ ./go -t -v go go
OPTIND: 3
4
go
go
[getopts]$ ./go -d -v go go
./go: illegal option -- d
Usage
[getopts]$ ./go -t go -v go -d
OPTIND: 2
5
go
All opts up front, please.
Usage
Try the following workaround:
# Parse the arguments.
while getopts ':h?f:' opts; do
case ${opts} in
f) # Foo argument.
;;
# My other arguments.
\? | h | *) # Prints help.
grep " .)\ #" $0
exit 0
;;
esac
done
So basically -?/-h would print the parameters with comments based on its own source. Specifying : before options will print help for any other unknown argument also.
v:t:r:s:c: should be in double quotes
"v:t:r:s:c:"
Based on the script you posted, maybe you don't require all those colons :
Also you don't need *)
You need to provide a leading colon in the getopts option string if you want to enable ? to match an invalid option -- :vtrsc. Also you don't need the backslash before the ?

shell script arguments non positional

Is there a way to feed non positional arguments to a shell script?
Meaning explicitly specify some kind of flag?
. myscript.sh value1 value2
. myscript.sh -val1=value1 -val2=value2
You can use getopts, but I don't like it because it's complicated to use and it doesn't support long option names (not the POSIX version anyway).
I recommend against using environment variables. There's just too much risk of name collision. For example, if your script reacts differently depending on the value of the ARCH environment variable, and it executes another script that (unbeknownst to you) also reacts to the ARCH environment variable, then you probably have a hard-to-find bug that only shows up occasionally.
This is the pattern I use:
#!/bin/sh
usage() {
cat <<EOF
Usage: $0 [options] [--] [file...]
Arguments:
-h, --help
Display this usage message and exit.
-f <val>, --foo <val>, --foo=<val>
Documentation goes here.
-b <val>, --bar <val>, --bar=<val>
Documentation goes here.
--
Treat the remaining arguments as file names. Useful if the first
file name might begin with '-'.
file...
Optional list of file names. If the first file name in the list
begins with '-', it will be treated as an option unless it comes
after the '--' option.
EOF
}
# handy logging and error handling functions
log() { printf '%s\n' "$*"; }
error() { log "ERROR: $*" >&2; }
fatal() { error "$*"; exit 1; }
usage_fatal() { error "$*"; usage >&2; exit 1; }
# parse options
foo="foo default value goes here"
bar="bar default value goes here"
while [ "$#" -gt 0 ]; do
arg=$1
case $1 in
# convert "--opt=the value" to --opt "the value".
# the quotes around the equals sign is to work around a
# bug in emacs' syntax parsing
--*'='*) shift; set -- "${arg%%=*}" "${arg#*=}" "$#"; continue;;
-f|--foo) shift; foo=$1;;
-b|--bar) shift; bar=$1;;
-h|--help) usage; exit 0;;
--) shift; break;;
-*) usage_fatal "unknown option: '$1'";;
*) break;; # reached the list of file names
esac
shift || usage_fatal "option '${arg}' requires a value"
done
# arguments are now the file names
The easiest thing to do is pass them as environment variables:
$ val1=value1 val2=value2 ./myscript.sh
This doesn't work with csh variants, but you can use env if you are using such a shell.
Yes there is. The name is getopts http://www.mkssoftware.com/docs/man1/getopts.1.asp
Example:
#!/bin/bash
while getopts d:x arg
do
case "$arg" in
d) darg="$OPTARG";;
x) xflag=1;;
?) echo >&2 "Usage: $0 [-x] [-d darg] files ..."; exit 1;;
esac
done
shift $(( $OPTIND-1 ))
for file
do
echo =$file=
done
Script has arguments as follows:
- $0 - script name
- $1, $2, $3.... - received arguments
$* = all arguments,
$# = number of arguments
Reference:
http://famulatus.com/ks/os/solaris/item/203-arguments-in-sh-scripts.html

Resources