Using grep/sed to parse BASH cmdline arguments - bash

I got a script that expects two args (filename and MD5hashval). I can extract just the hex output of MD5sum using md5sum test.sh | grep -om1 '^[0-9a-f]*.' For some reason, the same cmd fails when invoked from a script. Whats the best way to check cmdline arguments passed to a Bash script? Here's what the code looks like:
#!/bin/bash
while getopts ":f:s" opt; do
case $opt in
f)
FILENAME=`echo $OPTARG | sed 's/[-a-zA-Z0-9]*=//'`
echo ${FILENAME}
;;
s)
MD5SUM=`echo $OPTARG | grep -om1 '^[0-9a-f]*'`
echo $MD5SUM
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done

Since the s option requires an argument, you need to place a colon after it. It should be:
while getopts "f:s:" opt; do
...
From the getopts man page:
if a character is followed by a colon, the option is expected
to have an argument, which should be separated from it by white space.

My first action would be to place a debug line before your actual command:
echo "[$OPTARG]"
MD5SUM=`echo $OPTARG | grep -om1 '^[0-9a-f]*'`
But it actually has to do with the fact that s is not followed by a colon in your getopts options string. You should use f:s: instead:
... optstring contains the option characters to be recognized; if a character is followed by a colon, the option is expected to have an argument, which should be separated from it by white space.
And, just as an aside, I think your error lines should be -$opt rather than -$OPTARG.

Related

Failing to read value multi-word command line argument in bash

I am working on writing a bash shell script that has 4 command-line arguments. 3/4 arguments are one word and 1/4 is multi-word. I managed to get values for 2 of them, but can't get right values for 3rd and fourth. If I remove multi-work argumnent however, it works.
options=$(getopt -l "help,env:,site:,cluster:,cluster-group:" -o "he:s:c:cg:" -a -- "$#")
eval set -- "$options"
while true
do
case $1 in
-h|--help)
showHelp
exit 0
;;
-e|--env)
shift
export environment=$1
;;
-s|--site)
shift
export site=$1
;;
-c|--cluster)
shift
export cluster=$1
;;
-cg|--cluster-group)
shift
export cluster_group=$1
;;
--)
shift
break;;
esac
shift
done
echo $environment
echo $site
echo $cluster
echo $cluster_group
When ran sh b.sh -s S1 -e E1 -c C1 -cg CG1, output is
E1
S1
g
What am I doing wrong here?
As per man getopt(1): -o recognizes only one-character options.
-o, --options shortopts
The short (one-character) options to be recognized. If
this option is not found, the first parameter of getopt
that does not start with a '-' (and is not an option
argument) is used as the short options string. Each short
option character in shortopts may be followed by one colon
to indicate it has a required argument, and by two colons
to indicate it has an optional argument. The first
character of shortopts may be '+' or '-' to influence the
way options are parsed and output is generated (see
section SCANNING MODES for details).
So in your case ,you could mention only a single letter, something like :g after -o option for the cluster-group.
options=$(getopt -l "help:,env:,site:,cluster:,cluster-group:" -o "h:e:s:c:g:" -a -- "$#")

What is the correct way to check for a '?' in a case statement

I have below bash script using getopts. My code works as expected, i.e.,when an invalid option is specified, $myopt variable is set to '?' character and the case statement currectly processes the code matching the '?'.
Else where I see similar code using '\?' instead of just '?' in the case statement (please see the last three commented lines of the code block).
Is there any specific reason why the'?' is escaped ? I am aware any charcter can be escaped using '\' but my code works perfectly fine without escaping the '?'.
#!/bin/bash
while getopts ":abc" myopt; do
case $myopt in
[a-c])
echo "$myopt is a valid option!" >&2
;;
?)
echo "$OPTARG is an invalid option!" >&2
;;
# \?)
# echo "$OPTARG is an invalid option!" >&2
# ;;
esac
done
For the benefit of other reading this, below code will only work if '?' is escaped (see the accepted answer for a good explanation).
#!/bin/bash
while getopts ":abc" myopt; do
case $myopt in
\?)
echo "$OPTARG is an invalid option!" >&2
;;
[a-c])
echo "$myopt is a valid option!" >&2
;;
esac
done
if the script file is myscript.sh it can be tested like this -
./myscript.sh -a -z -a
For the Bash case statement the ? is a wildcard matching any single character/digit.
If you escape it with \? it specifically is trying to match the '?' char.
The case will continue to find a match, so in this case if the variable isn't [a-c] (a, b or c) then it will match if it the variable is 1 char.

bash - getopts in switch case

I'm trying to use getopts inside a switch case loop.
if i use only getopts or only the switch case it's work, however when i combine those two the getopts dos not trigger.
i have search a lot but i cat fins any mention for how to combine them, and problem i missing something stupid so for give me ...
here is the code essence.
#!/bin/bash
case $1 in
ver)
echo "vesion"
exit 0
;;
op)
while getopts ":a" opt; do
case $opt in
a)
echo "-a was triggered!" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done
;;
esac
when i do that
# bash -x test.sh op -a
i get
+ case $1 in
+ getopts :a opt
(and without debug i get nothing)
what is that that i missing to combine these two
Thanks :)
You should add a shift instruction at the beginning of your op) choice, before the call to getopts, to eat the op argument itself. Else, the first argument that getopts will analyze is op and it will silently stop (end of options).

Using wildcard inside case statement in bash

I'm making a simple bash script. It accepts 2 options (-d and -f). I would like to allow also the long version of this options (-directory and -file). I tried using the curly brackets wildcard but it doesn't work. Any suggestions?
Thanks!
#!/bin/bash
while test $# != 0
do
case $1 in
-f)
# do something
;;
{-d,-directory})
# do something
;;
*)
echo "error"
;;
esac
shift
done
Use a pipe symbol:
case $1 in
-f)
# do something
;;
-d|-directory)
# do something
;;
*)
echo "error"
;;
esac
The various options within a case statement are all shell script patterns, where a pattern can actually be several patterns separated by this pipe symbol |. You can also use regular expressions in this pattern, for example
-[dD]|-directory)
would match both -d, -D and -directory.
You can use
-d|-directory)
# do something
;;
But you should think about using a double dash to match 'long options' like in --directory.

How to handle shell getopts with parameter containing blank spaces

I'm looking for a way to handle arguments containing blank spaces that has to be parsed
by shell getopts command.
while getopts ":a:i:o:e:v:u:" arg
do
echo "ARG is: $arg" >> /tmp/submit.log
case "$arg" in
a) arg1="$OPTARG" ;;
i) arg2="$OPTARG" ;;
o) arg3="$OPTARG" ;;
...
u) argn="$OPTARG" ;;
-) break ;;
\?) ;;
*) echo "unhandled option $arg" >> /tmp/submit.log ;;
?) echo $usage_string
exit 1 ;;
esac
done
Now if -u has argument like "STRING WITH WHITE SPACE"
than just the first part of the string is triggered and the while loop doesn't go to the end.
many thanks.
a trap for young players (ie me!)
beware a line like this:
main $#
what you really need is:
main "$#"
otherwise getopts will mince up your options into little pieces
http://www.unix.com/shell-programming-scripting/70630-getopts-list-argument.html
As Mat notes, your script fragment is already correct. If you're invoking your script from a shell, you need to quote arguments properly, e.g.
myscript -u "string with white space"
myscript -u 'string with white space'
myscript -u string\ with\ white\ space
myscript -u string' w'ith\ "whi"te" "''space
Requiring these quotes is not a defect in your script, it's the way the calling shell works. All programs, scripts or otherwise, receive arguments as a list of strings. The quotes in the calling shell are used to sort these arguments into separate “words” (list elements). All the calls above (made from a unix shell) pass a list of three strings to the script: $0 is the script name (myscript), $1 is -u and $2 is the string string with white space.

Resources