Reputation: 10882
I have a Bash function f
, which takes a string value s
as one of its arguments.
I need to abort execution if s
is not an integer between [1000-9999]
.
I initially thought I would try to cast the string to an int and perform arithmetic range checks on the result, aborting on errors, but all other parameters are checked using RegEx so I might as well be consistent. I have to, actually.
Bash version: 4+
I could not come up with a better-looking pattern than this:
[[ "$s" =~ [[:\<:]][[:digit:]]{4}[[:\>:]] ]]
Can you help me improve the readability of the pattern-check construct above?
What I particularly dislike in the pattern above is:
<
as [[:\<:]]
One idea I had was to use full-line boundary checks, although that's a hack? (i.e. using ^...$ rather than \b...\b)
Optionally :: Is there a fairly well-established pattern for argument validation in Bash?
Upvotes: 0
Views: 501
Reputation: 6577
See http://mywiki.wooledge.org/BashFAQ/054
You could do this using only a pattern match but I'd split it. I would say something like this is close to the "usual" method:
f() {
if [[
$1 == +([[:digit:]]) &&
"$1 >= 1000 && $1 < 10000" -ne 0
]]
then
...
fi
}
In Bash/ksh test expression compound commands, the arguments to numerical comparison operators are evaluated as arithmetic expressions. The double quotes allow for whitespace within the expression, otherwise the parser can't distinguish some arithmetic operators from test expression operators. Testing for whether the expression on the left is 0
is basically equivalent to (())
. This is identical to @chepner's answer, except using only one command. I'd expect this performs slightly better and I'm used to the grammar so it's pretty clear to me. Some prefer the other way. You should only use the [[
arithmetic operators over ((
when combined with some other test.
You have to first validate before using any unpredictable input as an arithmetic expression. Then you can check for whether it's within a certain range. There are other ways to do this, but this is how I would do it personally. It's clear and works for all permutations of this problem.
I should also add that if your input might contain octal or hex literals, some further processing is needed. Usually running the input through printf %d
works well in that case.
Upvotes: 1
Reputation: 530902
bash
can do integer comparisons:
shopt -s extglob # Needed for the extended pattern +(...)
f () {
if [[ $1 == +([[:digit:]]) ]] &&
(( $1 >= 1000 && $1 < 10000 )); then
...
fi
}
Upvotes: 2
Reputation: 9926
Also, I would try:
[[ $s =~ ^[1-9][0-9]{3}$ ]]
Otherwise 0999
would pass. But an other aspect might be that +1234
would probably be valid input, and so would perhaps be 1234.0
, so if you need to be complete you may need to expand your regex, or perform value checking after all..
Upvotes: 2