[SOLVED] How to restrict user input in bash via the 'read' statement to a range of numbers [0-9]..
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
How to restrict user input in bash via the 'read' statement to a range of numbers [0-9]..
Im writing a shell script to mirror a website via wget (the right way) but in parts of the script I need to make sure the script will only accept a range of numbers as valid input (i.e. 1-10)... E.g. I know how to make it make sure the script only looks for 'Y' (for yes) or 'n' (for no), but I cant figure out how to specify a range...
I tried [0..9], [0-9] and both dont work...
The following is what I tried:
Code:
until [[ $levels = [0-9] || $levels = 'inf' ]]; do
read -p "How many levels to descend? (E.g., '5' or type 'inf' for infinite): " levels
done
Bash has a regex comparison operator "=~" that accepts character classes. However it is not exclusive, so you'll need to use anchors to ensure only digits.
A apologize for posting my entire script above (now removed) and confusing you guys, here is a short snippet
detailing what i'm trying to do (I want it so that the script only accepts numbers '0-9' or the word 'inf').
Code:
until [[ $levels = [0-9] || $levels = 'inf' ]]; do
read -p "How many levels to descend? (E.g., '5' or type 'inf' for infinite): " levels
done
Quote:
Originally Posted by NevemTeve
Maybe it would be better to use command-line parameters, so that your script could work in background, without user-interaction.
In the script it asks if the user wants to do an "Advanced Download" which is a series of questions that requires user interaction.
Quote:
Originally Posted by syg00
Bash has a regex comparison operator "=~" that accepts character classes. However it is not exclusive, so you'll need to use anchors to ensure only digits.
Can show me an example please, I dont know what anchors are in a bash context.
This should do what you want within the context of your example:
Code:
until [[ $levels =~ ^[0-9]$ || $levels = 'inf' ]]; do
The full details may be found in man bash under Compound Operators, from which this excerpt:
Code:
[[ expression ]]
Return a status of 0 or 1 depending on the evaluation of the conditional expression expression.
Expressions are composed of the primaries described below under CONDITIONAL EXPRESSIONS. Word
splitting and pathname expansion are not performed on the words between the [[ and ]]; tilde
expansion, parameter and variable expansion, arithmetic expansion, command substitution, process
substitution, and quote removal are performed. Conditional operators such as -f must be unquoted
to be recognized as primaries.
...
An additional binary operator, =~, is available, with the same precedence as == and !=. When it
is used, the string to the right of the operator is considered an extended regular expression and
matched accordingly (as in regex(3)).
What is meant by the term anchor is the ^ and $ which anchor the expression to the beginning and end of the string being matched and exclude matches such as 'abc4xyz' and '1234'. See the referenced regex man page for details on the regular expression syntax.
And Welcome to LQ!
Last edited by astrogeek; 05-22-2019 at 02:33 PM.
Reason: typos
I wasn't sure how to integrate those regex wildcards (and didnt those were also called anchors)
They are not wildcards - they have a specific function of string location. Others exist.
Don't call them wildcards - especially when asking for assistance; you will confuse everybody and delay getting a good answer.
They are not wildcards - they have a specific function of string location. Others exist.
Don't call them wildcards - especially when asking for assistance; you will confuse everybody and delay getting a good answer.
Ahh ok I understand now, yeah cause their not being used a substitute for another character, which are what wildcards are for..
And thanks for telling me about '=~' as it didnt work until I did that...
Code:
until [[ $levels =~ ^[0-8]$ || $levels = 'inf' ]]; do
read -p "How many levels to descend? (E.g., '5' or type 'inf' for infinite): " levels
done
The traditional method I've always seen for a persistent user prompt is to set it in an infinite loop, and exit it when you get what you want. The code is a bit longer, but it's easier to read and more flexible than attempting to directly test the input in the loop.
Code:
while true; do
echo
echo "How many levels to descend?"
echo "(Enter a number from 0 to 9 or type 'inf' for infinite)"
echo
read -p 'Number: ' level
case $level in
[0-9]) echo "You chose number $level"
break
;;
inf) echo "You chose 'infinite'"
break
;;
*) echo "'$level' is not a valid input. Try again."
;;
esac
done
Edit: Let's not forget the select loop either, which is built-in variation of the above, useful for generating simple menu-based prompts. I don't use it often because it doesn't allow for much formatting freedom, but it can be useful for quick-and-dirty queries. You can easily find descriptions of its use on the net.
Last edited by David the H.; 05-27-2019 at 09:02 AM.
Reason: as stated
^ If I did that for every question then my scripts would be books. But I do [do] that sometimes for primary questions that lead to secondary questions, cause as you said allows for that flexability with getopts and such... I have more rececently used and heard of the select operator before to setup simple menus, and noticed the formatting is basic, but get's the job done..
Location: Fleury-les-Aubrais, 120 km south of Paris
Distribution: Devuan, Debian, Mandrake, Freeduc (the one I used to work on), Slackware, MacOS X
Posts: 251
Rep:
Errghh
Quote:
Originally Posted by David the H.
The traditional method I've always seen for a persistent user prompt is to set it in an infinite loop, and exit it when you get what you want. The code is a bit longer, but it's easier to read and more flexible than attempting to directly test the input in the loop.
Wasn't the OP code a loop already*?
And if I'm not wrong, the proposed code only allow values: 01, 1, 2, 3, 4, 5, 6, 7, 8. If "10" is a valid answer, the regex should be "^[0-9]+$". I don't think Bash accepts extended regexes, otherwise we could mix the two conditions in one regex. When I need to use extended regexes, I use the exit code of a "grep -E" to make the test.
Quote:
Edit: Let's not forget the select loop either, which is built-in variation of the above, useful for generating simple menu-based prompts. I don't use it often because it doesn't allow for much formatting freedom, but it can be useful for quick-and-dirty queries. You can easily find descriptions of its use on the net.
Yes, it's a loop, but it's a loop that prompts for input in the inner part, while testing it in the outer part, which can be somewhat awkward to handle properly. Moving all the vital commands into the inner command list and inverting the logic (assume failure until you get what you want) makes it more robust, flexible and transparent. The results are the same, but the workflow is safer and easier to handle.
Quote:
And if I'm not wrong, the proposed code only allow values: 01, 1, 2, 3, 4, 5, 6, 7, 8. If "10" is a valid answer, the regex should be "^[0-9]+$". I don't think Bash accepts extended regexes, otherwise we could mix the two conditions in one regex. When I need to use extended regexes, I use the exit code of a "grep -E" to make the test.
My example was just a proof-of-concept, using the numbers from the code in the OP. You don't have to use case, although it is probably the most common and most efficient choice most of the time. You can use whatever input tests suit your needs, as simple or complex as you want them to be, just as long as you can break when you need to. That's part of the beauty of putting them in inside the loop.
And bash does have the ability to use extended regex forms, with the [[ =~ ]] test pattern. It also has the regex-like extended globbing patterns. case globbing also supports "|" (or) separators for multiple patterns at once.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.