File globbing and matching numbers only - bash

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

Related

How to pattern match a script argument in Linux 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.

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 (*\?)?

Linux Regular Expression

I'm working with shell scripting in Linux. I want to check if the value of MAX_ARCHIVE_AGE is numeric or not. My code is like this:
MAX_ARCHIVE_AGE = "50"
expr="*[0-9]*"
if test -z "$MAX_ARCHIVE_AGE";
then
echo "MAX_ARCHIVE_AGE variable is missing or not initiated"
else
if [ "$MAX_ARCHIVE_AGE" != $expr ]
then
echo "$MAX_ARCHIVE_AGE is not a valid value"
fi
fi
I want to match the value of MAX_ARCHIVE_AGE with my expr. Please help.
For POSIX compatibility, look at case. I also find it more elegant than the corresponding if construct, but the syntax may seem a bit odd when you first see it.
case $MAX_ARCHIVE_AGE in
'' ) echo "empty" >&2 ;;
*[!0-9]* ) echo "not a number" >&2 ;;
esac
By the way, notice the redirection of error messages to standard error with >&2.
Your expr will match anything that contains any digits; it's better to check if it contains only digits, or conversely, to check if it contains any non-digits. To do that, you can write:
if ! [[ "$MAX_ARCHIVE_AGE" ]] ; then
echo "MAX_ARCHIVE_AGE is blank or uninitialized" >&2
elif [[ "$MAX_ARCHIVE_AGE" == *[^0-9]* ]] ; then
echo "$MAX_ARCHIVE_AGE is not a valid value" >&2
fi
Also, note that you would initialize MAX_ARCHIVE_AGE by writing e.g. MAX_ARCHIVE_AGE=50 (no spaces), not MAX_ARCHIVE_AGE = 50. The latter tries to run a program called MAX_ARCHIVE_AGE with the arguments = and 50.

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 .

Resources