LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-21-2019, 11:16 PM   #1
proMusic
LQ Newbie
 
Registered: May 2019
Posts: 20

Rep: Reputation: Disabled
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

Last edited by proMusic; 05-22-2019 at 02:15 PM.
 
Old 05-22-2019, 12:45 AM   #2
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
Maybe it would be better to use command-line parameters, so that your script could work in background, without user-interaction.
 
1 members found this post helpful.
Old 05-22-2019, 01:13 AM   #3
syg00
LQ Veteran
 
Registered: Aug 2003
Location: Australia
Distribution: Lots ...
Posts: 21,126

Rep: Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120
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.
 
1 members found this post helpful.
Old 05-22-2019, 02:01 PM   #4
proMusic
LQ Newbie
 
Registered: May 2019
Posts: 20

Original Poster
Rep: Reputation: Disabled
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 View Post
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 View Post
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.

Last edited by proMusic; 05-22-2019 at 02:11 PM.
 
Old 05-22-2019, 02:26 PM   #5
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,263
Blog Entries: 24

Rep: Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194
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
 
2 members found this post helpful.
Old 05-22-2019, 02:33 PM   #6
proMusic
LQ Newbie
 
Registered: May 2019
Posts: 20

Original Poster
Rep: Reputation: Disabled
^ Sweet thanks, I wasn't sure how to integrate those regex wildcards (and didnt know those were also called anchors).. Ok thanks again!!

Last edited by proMusic; 05-22-2019 at 08:47 PM.
 
Old 05-22-2019, 03:44 PM   #7
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,263
Blog Entries: 24

Rep: Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194
You are welcome!

And thanks for both the edit of the original post and marking the thread solved - greatly appreciated!
 
1 members found this post helpful.
Old 05-22-2019, 06:40 PM   #8
syg00
LQ Veteran
 
Registered: Aug 2003
Location: Australia
Distribution: Lots ...
Posts: 21,126

Rep: Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120Reputation: 4120
Quote:
Originally Posted by proMusic View Post
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.
 
2 members found this post helpful.
Old 05-22-2019, 08:46 PM   #9
proMusic
LQ Newbie
 
Registered: May 2019
Posts: 20

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by syg00 View Post
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
 
Old 05-27-2019, 08:48 AM   #10
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
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
 
Old 05-27-2019, 06:33 PM   #11
proMusic
LQ Newbie
 
Registered: May 2019
Posts: 20

Original Poster
Rep: Reputation: Disabled
^ 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..

Last edited by proMusic; 05-27-2019 at 07:01 PM.
 
Old 06-04-2019, 07:00 AM   #12
Stéphane Ascoët
Member
 
Registered: Feb 2004
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: Reputation: 49
Arrow Errghh

Quote:
Originally Posted by David the H. View Post
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.
Yeah, I never think about this one neither.
 
Old 07-04-2019, 12:52 PM   #13
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
Quote:
Originally Posted by Stéphane Ascoët View Post
Wasn't the OP code a loop already*?
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.
 
Old 07-04-2019, 02:43 PM   #14
MadeInGermany
Senior Member
 
Registered: Dec 2011
Location: Simplicity
Posts: 2,791

Rep: Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201
The following has the correct order, and does not need break (but needs a no-op command like : or an echo error command).
And an ERE works.
Code:
until
  read -p "How many levels to descend? (E.g., '5' or 'inf' for infinite): " levels
  [[ $levels =~ ^([0-9]+|inf)$ ]]
do
  :
done
 
1 members found this post helpful.
Old 07-04-2019, 03:45 PM   #15
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,263
Blog Entries: 24

Rep: Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194Reputation: 4194
Quote:
Originally Posted by MadeInGermany View Post
The following has the correct order, and does not need break (but needs a no-op command like : or an echo error command).
And an ERE works.
Code:
until
  read -p "How many levels to descend? (E.g., '5' or 'inf' for infinite): " levels
  [[ $levels =~ ^([0-9]+|inf)$ ]]
do
  :
done
Very nice! My new knowledge jewel of the day - now in the treasure chest!
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] shell scripting - selecting a range of numbers sryzdn Linux - Newbie 2 06-28-2014 02:47 AM
Scripting: How to verifiy if input matches a range of numbers? klss Linux - Software 3 03-11-2009 04:57 AM
Shell scripting - Random numbers within a range felixc Linux - Newbie 2 10-09-2005 05:41 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 04:38 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration