How to pattern match a script argument in Linux bash - bash

Basically, I have to create a bash script that analyzez the arguments of the script and if it's only one, proceeds to pattern match it. If it starts with a number, it says so, if it starts with a letter, it says so and if neither, it says the argument is a string. If you didn't provide an argument, it says you need 1, or if you provided 2 or more. Everything works, apart from the pattern matching itself. I always get the default case. What's wrong with it?
I gotta use case :( .
case $# in
1) case $1 in
/^?[0-9][a-zA-z]*$/) echo "Argument starts with number";;
/^?[a-zA-z][a-zA-z]*$/) echo "Argument starts with letter";;
*) echo "Argument is a string";;
esac;;
0) echo -n "You can't use 0 arguments"
exit 1;;
*) echo -n "You can't use 2 or more arguments"
exit 1;;
esac
exit 0
EDIT: typing this in terminal gives me the default case: l#ubuntu:~/Documents$ ./e4_G_L.sh contor - maybe I wrote the argument wrong and the pattern matching works just fine?

As pointed out in the comments, case only supports globs (also known as wildcards). To check regular expression as in your script you could use [[ … =~ … ]] but that would be overkill. Since you only want to check the first letter, globs are sufficient.
Also, I would rephrase the warning message. Tell the user what to do, not just “You messed up. Good luck guessing on your next try.”.
if [ $# != 1 ]; then
echo "Expected 1 argument but found $#."
exit 1
fi
case "$1" in
[0-9]*) echo "Argument starts with number" ;;
[a-zA-Z]*) echo "Argument starts with letter" ;;
*) echo "Argument is a string";;
esac

Using bash extended patterns:
shopt -s extglob
case $# in
0) echo "You can't use 0 arguments"
exit 1
;;
1) case $1 in
[0-9]+([[:alpha:]]) ) echo "A number followed by letters" ;;
+([[:alpha:]]) ) echo "Only letters" ;;
*) echo "Something else" ;;
esac
;;
*) echo "You can't use 2 or more arguments"
exit 1
;;
esac

Thanks to everybody who bothered to help this lost soul :). It's a task for my Uni so no need for complex hints. The teacher knows what this is about so keeping it short. Ended up using the first answer.

Related

How to parse invalid options?

I am writing the following script to parse some options:
#!/bin/bash
while [[ $# > 1 ]]
do
key="$1"
case $key in
-i|--inbound)
inbound="true"
shift # past argument
;;
-o|--outbound)
outbound="true"
shift # past argument
;;
*)
echo "hola"
exit 1
;;
esac
shift # past argument or value
done
echo $inbound
echo $outbound
The problem is that i would like to terminate the program, if I receive an invalid option, I tried the following:
*)
exit 1
echo "invalid option"
;;
but when i run the program like this: bash script.sh -invalid, anything happens, i would like to appreciate any suggestion to fix this, my desired output would like to:
invalid option
while [[ $# > 1 ]]
should be
while (($# >= 1))
You want a numeric comparison, not a string comparison. Although in this case, it doesn't make any difference, it would make a big difference if you had compared with 2, since the string 10 is less than the string 2.
Anyway, if you invoke your script with one argument, $# will be one. So the greater-than comparison is not correct.
Finally, if you really had:
exit 1
echo "hola"
the echo would never be executed, because the exit would have happened first.

bash script case statement needs to detect specific arguments

I have to write this script where it will display each entry that is entered in on its own line and separate each line with "*****". I've already got that down for the most part but now I need it to detect when the word "TestError" and/or "now" is entered in as an argument. The way it is setup right now it will detect those words correctly if they are the first argument on the line, I'm just not sure how to set it up where it will detect the word regardless of which argument it is on the line. Also I need help with the *? case where I need it to say "Do not know what to do with " for every other argument that is not "TestError" or "now", at the moment it will do it for the first argument but not the rest.
Would it work the way it is right now? Or would I have to use only the *? and * cases and just put an if/then/else/fi statement in the *? case in order to find the "TestError" "now" and any other argument.
# template.sh
function usage
{
echo "usage: $0 arguments ..."
if [ $# -eq 1 ]
then echo "ERROR: $1"
fi
}
# Script starts after this line.
case $1 in
TestError)
usage $*
;;
now)
time=$(date +%X)
echo "It is now $time"
;;
*?)
echo "My Name"
date
echo
usage
printf "%s\n*****\n" "Do not know what to do with " "$#"
;;
*)
usage
;;
esac
You'll need to loop over the arguments, executing the case statement for each one.
for arg in "$#"; do
case $arg in
TestError)
usage $*
;;
now)
time=$(date +%X)
echo "It is now $time"
;;
*?)
echo "My Name"
date
echo
usage
printf "%s\n*****\n" "Do not know what to do with " "$#"
;;
*)
usage
;;
esac
done
* and *? will match the same strings. Did you mean to match the ? literally (*\?)?

Having getopts to show help if no options provided

I parsed some similar questions posted here but they aren't suitable for me.
I've got this wonderful bash script which does some cool functions, here is the relevant section of the code:
while getopts ":hhelpf:d:c:" ARGS;
do
case $ARGS in
h|help )
help_message >&2
exit 1
;;
f )
F_FLAG=1
LISTEXPORT=$OPTARG
;;
d )
D_FLAG=1
OUTPUT=$OPTARG
;;
c )
CLUSTER=$OPTARG
;;
\? )
echo ""
echo "Unimplemented option: -$OPTARG" >&2
echo ""
exit 1
;;
: )
echo ""
echo "Option -$OPTARG needs an argument." >&2
echo ""
exit 1
;;
* )
help_message >&2
exit 1
;;
esac
done
Now, all my options works well, if triggered. What I want is getopts to spit out the help_message function when no option is triggered, say the script is launched just ./scriptname.sh without arguments.
I saw some ways posted here, implementing IF cycle and functions but, since I'm just starting with bash and I already have some IF cycles on this script, I would like to know if there is an easier (and pretty) way to to this.
If you just want to detect the script being called with no options then just check the value of $# in your script and exit with a message when it is zero.
If you want to catch the case where no option arguments are passed (but non-option arguments) are still passed then you should be able to check the value of OPTIND after the getopts loop and exit when it is 1 (indicating that the first argument is a non-option argument).
Many thanks to Etan Reisner, I ended up using your suggestion:
if [ $# -eq 0 ];
then
help_message
exit 0
else
...... remainder of script
This works exactly the way I supposed.
Thanks.
Dropping a sample code from #Etan Reisner's statement:
you want to catch the case where no option arguments are passed (but non-option arguments) are still passed then you should be able to check the value of OPTIND after the getopts loop and exit when it is 1 (indicating that the first argument is a non-option argument).
# Check if any arguments were passed
if [ $OPTIND -eq 1 ]; then
help_message
exit 1
fi
# ........ the rest of the script
Just in case someone wanted a sample visual 😅

Bash case statement

I'm trying to learn case as I was to write a fully functional script.
I'm starting off with the below
#!/bin/sh
case $# in
-h|--help)
echo "You have selected Help"
;;
-B|-b)
echo "You have selected B"
;;
-C|-c)
echo "You have selected C"
;;
*)
echo "Valid Choices are A,B,C"
exit 1
;;
esac
I want to use two of these options:
./getopts.sh -h -c
But i get this result
Valid Choices are A,B,C
Please can you help out and let me know what I'm doing wrong?
I want to build a script that will do something if you enter one option but do multiple things if you enter multiple.
Also how would i parse $1 to this script as surley which ever option i enter first (-h) will be $1 ??
Thanks!
Try this
#!/bin/sh
usage() {
echo `basename $0`: ERROR: $* 1>&2
echo usage: `basename $0` '[-a] [-b] [-c]
[file ...]' 1>&2
exit 1
}
while :
do
case "$1" in
-a|-A) echo you picked A;;
-b|-B) echo you picked B;;
-c|-C) echo you picked C;;
-*) usage "bad argument $1";;
*) break;;
esac
shift
done
Using getopt or getopts is the better solution. But to answer your immediate question, $# is all of your arguments, so -h -c, which doesn't match any of the single-argument patterns in your case statement. You would still need to iterate over your arguments like so
for arg in "$#"; do
case $arg in
....
esac
done
to parse the positional arguments like ... $1 , just use $1 in the case stmt and then at the end ... use shift to pust the 2nd arg to $1 and likewise .
also i would put the case stmt in a while loop or better a fxn so that i can run it twice for the two options or the number of options ..........
$# will let you know how many options/arguments were there .

File globbing and matching numbers only

In a bash script I need to verify that the user inputs actual numbers so I have thought the easiest way to make myself sure about that is implementing a case:
case $1 in
[0-9]*)
echo "It's ok"
;;
*)
echo "Ain't good!"
exit 1
;;
esac
But I'm having hard time with file globbing because I can't find a way to demand the $1 value has to be numeric only. Or another way could be excluding all the alternatives:
case $1 in
-*)
echo "Can't be negative"
exit 1
;;
+*)
echo "Must be unsigned"
exit 1
;;
*[a-zA-z]*)
echo "Can't contain letters"
exit 1
;;
esac
The thing is in this case I should be able to block "special" chars like ! ? ^ = ( ) and so forth... I don't know how to acheive it. Please anyone give me a hint?
Actually it would be better to use
*[!0-9]*
instead of
*[^0-9]*
as the first one is POSIX and the second one is a bashism[1].
[1] http://rgeissert.blogspot.com/2013/02/a-bashism-week-negative-matches.html
If you find a non-numeric character anywhere in the string, the input is bad, otherwise it's good:
case "$1" in
*[^0-9]*) echo "first parameter must contain numbers only"; exit 1;;
esac

Resources