Matching numbers with regex in case statementWhy does my regular expression work in X but not in Y?Number of...
What do the "optional" resistor and capacitor do in this circuit?
Can anyone give me examples of the relative-determinative 'which'?
tikz drawing rectangle discretized with triangle lattices and its centroids
It is as easy as A B C, Figure out U V C from the given relationship
Why would someone open a Netflix account using my Gmail address?
History of the Frobenius Endomorphism?
With today's technology, could iron be smelted at La Rinconada?
Understanding Python syntax in lists vs series
Could a space colony 1g from the sun work?
Break long word (not long text!) in longtable cell
Did any "washouts" of the Mercury program eventually become astronauts?
Will consteval functions allow template parameters dependent on function arguments?
How does a permutation act on a string?
How to not get blinded by an attack at dawn
Why doesn't Iron Man's action affect this person in Endgame?
Why do OOK transmissions have bandwidth?
Does the Rogue's Reliable Talent feature work for thieves' tools, since the rogue is proficient in them?
When did game consoles begin including FPUs?
Formal Definition of Dot Product
Do high-wing aircraft represent more difficult engineering challenges than low-wing aircraft?
How to check if comma list is empty?
Cuban Primes
Does it matter what way the tires go if no directional arrow?
Does this "yield your space to an ally" rule my 3.5 group uses appear anywhere in the official rules?
Matching numbers with regex in case statement
Why does my regular expression work in X but not in Y?Number of backslashes needed for escaping regex backslash on the command-lineRegex for phrase matching with case statement in kshregex matching with “locate”Is there a special variable containing a case statement matchRegex in case statementbash script with case statement not returning an outputPass argument to function with case statementCase statement allow only alphabetic characters?Match wildcarded string in case statement using sh shellbash script with case statement conditions not getting executed
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
How to write a case statement using regex as condition (to match numbers)? I try few deferent ways I come up with (e.g. [0-9]+
or ^[0-9][0-9]*$
), non of them works. Like in following example the statement is matching the wildcard but not the numeric regex.
i=1
let arg_n=$#+1
while (( $i < $arg_n )); do
case ${!i} in
[0-9]+)
n=${!i}
;;
*)
echo 'Invalid argument!'
;;
esac
let i=$i+1
done
Output:
$ ./cmd.sh 64
Invalid argument!
bash regular-expression case
|
show 2 more comments
How to write a case statement using regex as condition (to match numbers)? I try few deferent ways I come up with (e.g. [0-9]+
or ^[0-9][0-9]*$
), non of them works. Like in following example the statement is matching the wildcard but not the numeric regex.
i=1
let arg_n=$#+1
while (( $i < $arg_n )); do
case ${!i} in
[0-9]+)
n=${!i}
;;
*)
echo 'Invalid argument!'
;;
esac
let i=$i+1
done
Output:
$ ./cmd.sh 64
Invalid argument!
bash regular-expression case
This variable indirection works just fine. I have more cases in the real script and it works. I'm trying to match any occurrence of real numbers in the program arguments. So 0 or 999 should match. Else if there is some invalid argument like '-x' or letters in stead of numbers, program shall match*
, at least thats what I thought.
– siery
Mar 21 '18 at 19:35
@John1024, numbers aren't valid names for variables, but they're quite valid for the names of the positional parameters, and${!i}
works fine for those. e.g.set -- aa bb cc; i=2; echo ${!i}
printsbb
– ilkkachu
Mar 21 '18 at 22:45
that said, the easier way to loop over the arguments to the script would be to just usefor val in "$@"; do ...
and use$val
in the loop
– ilkkachu
Mar 21 '18 at 22:46
Read unix.stackexchange.com/questions/119905/…
– Gilles
Mar 21 '18 at 22:56
@John1024, and when it's run,i
contains1
, so${!i}
is the same as$1
: it expands to the value of the first argument, be it64
orabc
or whatever. What they have is just a convoluted way of looping over the positional parameters / command line arguments.
– ilkkachu
Mar 21 '18 at 23:14
|
show 2 more comments
How to write a case statement using regex as condition (to match numbers)? I try few deferent ways I come up with (e.g. [0-9]+
or ^[0-9][0-9]*$
), non of them works. Like in following example the statement is matching the wildcard but not the numeric regex.
i=1
let arg_n=$#+1
while (( $i < $arg_n )); do
case ${!i} in
[0-9]+)
n=${!i}
;;
*)
echo 'Invalid argument!'
;;
esac
let i=$i+1
done
Output:
$ ./cmd.sh 64
Invalid argument!
bash regular-expression case
How to write a case statement using regex as condition (to match numbers)? I try few deferent ways I come up with (e.g. [0-9]+
or ^[0-9][0-9]*$
), non of them works. Like in following example the statement is matching the wildcard but not the numeric regex.
i=1
let arg_n=$#+1
while (( $i < $arg_n )); do
case ${!i} in
[0-9]+)
n=${!i}
;;
*)
echo 'Invalid argument!'
;;
esac
let i=$i+1
done
Output:
$ ./cmd.sh 64
Invalid argument!
bash regular-expression case
bash regular-expression case
edited 45 mins ago
siery
asked Mar 21 '18 at 19:21
sierysiery
3418
3418
This variable indirection works just fine. I have more cases in the real script and it works. I'm trying to match any occurrence of real numbers in the program arguments. So 0 or 999 should match. Else if there is some invalid argument like '-x' or letters in stead of numbers, program shall match*
, at least thats what I thought.
– siery
Mar 21 '18 at 19:35
@John1024, numbers aren't valid names for variables, but they're quite valid for the names of the positional parameters, and${!i}
works fine for those. e.g.set -- aa bb cc; i=2; echo ${!i}
printsbb
– ilkkachu
Mar 21 '18 at 22:45
that said, the easier way to loop over the arguments to the script would be to just usefor val in "$@"; do ...
and use$val
in the loop
– ilkkachu
Mar 21 '18 at 22:46
Read unix.stackexchange.com/questions/119905/…
– Gilles
Mar 21 '18 at 22:56
@John1024, and when it's run,i
contains1
, so${!i}
is the same as$1
: it expands to the value of the first argument, be it64
orabc
or whatever. What they have is just a convoluted way of looping over the positional parameters / command line arguments.
– ilkkachu
Mar 21 '18 at 23:14
|
show 2 more comments
This variable indirection works just fine. I have more cases in the real script and it works. I'm trying to match any occurrence of real numbers in the program arguments. So 0 or 999 should match. Else if there is some invalid argument like '-x' or letters in stead of numbers, program shall match*
, at least thats what I thought.
– siery
Mar 21 '18 at 19:35
@John1024, numbers aren't valid names for variables, but they're quite valid for the names of the positional parameters, and${!i}
works fine for those. e.g.set -- aa bb cc; i=2; echo ${!i}
printsbb
– ilkkachu
Mar 21 '18 at 22:45
that said, the easier way to loop over the arguments to the script would be to just usefor val in "$@"; do ...
and use$val
in the loop
– ilkkachu
Mar 21 '18 at 22:46
Read unix.stackexchange.com/questions/119905/…
– Gilles
Mar 21 '18 at 22:56
@John1024, and when it's run,i
contains1
, so${!i}
is the same as$1
: it expands to the value of the first argument, be it64
orabc
or whatever. What they have is just a convoluted way of looping over the positional parameters / command line arguments.
– ilkkachu
Mar 21 '18 at 23:14
This variable indirection works just fine. I have more cases in the real script and it works. I'm trying to match any occurrence of real numbers in the program arguments. So 0 or 999 should match. Else if there is some invalid argument like '-x' or letters in stead of numbers, program shall match
*
, at least thats what I thought.– siery
Mar 21 '18 at 19:35
This variable indirection works just fine. I have more cases in the real script and it works. I'm trying to match any occurrence of real numbers in the program arguments. So 0 or 999 should match. Else if there is some invalid argument like '-x' or letters in stead of numbers, program shall match
*
, at least thats what I thought.– siery
Mar 21 '18 at 19:35
@John1024, numbers aren't valid names for variables, but they're quite valid for the names of the positional parameters, and
${!i}
works fine for those. e.g. set -- aa bb cc; i=2; echo ${!i}
prints bb
– ilkkachu
Mar 21 '18 at 22:45
@John1024, numbers aren't valid names for variables, but they're quite valid for the names of the positional parameters, and
${!i}
works fine for those. e.g. set -- aa bb cc; i=2; echo ${!i}
prints bb
– ilkkachu
Mar 21 '18 at 22:45
that said, the easier way to loop over the arguments to the script would be to just use
for val in "$@"; do ...
and use $val
in the loop– ilkkachu
Mar 21 '18 at 22:46
that said, the easier way to loop over the arguments to the script would be to just use
for val in "$@"; do ...
and use $val
in the loop– ilkkachu
Mar 21 '18 at 22:46
Read unix.stackexchange.com/questions/119905/…
– Gilles
Mar 21 '18 at 22:56
Read unix.stackexchange.com/questions/119905/…
– Gilles
Mar 21 '18 at 22:56
@John1024, and when it's run,
i
contains 1
, so ${!i}
is the same as $1
: it expands to the value of the first argument, be it 64
or abc
or whatever. What they have is just a convoluted way of looping over the positional parameters / command line arguments.– ilkkachu
Mar 21 '18 at 23:14
@John1024, and when it's run,
i
contains 1
, so ${!i}
is the same as $1
: it expands to the value of the first argument, be it 64
or abc
or whatever. What they have is just a convoluted way of looping over the positional parameters / command line arguments.– ilkkachu
Mar 21 '18 at 23:14
|
show 2 more comments
3 Answers
3
active
oldest
votes
case
does not use regexes, it uses patterns
For "1 or more digits", do this:
shopt -s extglob
...
case ${!i} in
+([[:digit:]]) )
n=${!i}
;;
...
If you want to use regular expressions, use the =~
operator within [[...]]
if [[ ${!i} =~ ^[[:digit:]]+$ ]]; then
n=${!i}
else
echo "Invalid"
fi
add a comment |
As glenn says, “case
does not use regexes, it uses patterns”.
As bash(1) says,
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
Acase
command first expandsword
,
and tries to match it against eachpattern
in turn,
using the same matching rules as for pathname expansion
(see Pathname Expansion below).
Similarly, the POSIX specification says,
… each pattern … shall be compared against the expansion of word,
according to the rules described in Pattern Matching Notation …
So the patterns are pathname expansion patterns,
a.k.a. wildcards, a.k.a. globs, as in ls -l -- *.sh
or rm -- *.bak
.
Sure, shopt -s extglob
and [[ … =~ … ]]
are the neatest thing since sliced bread,
but they aren’t POSIX,
and it can be useful to know how to use the original tools.
For years, programmers checked, for example,
whether a string was a number
by checking whether it was not not a number.
You’ve defined a number to be a string that consists
(entirely) of one or more digits.
So a string is not a number if it is null,
or if it contains a character that is not a digit.
We can test these conditions with a case
statement as follows:
case "$1" in
("")
# null
︙
;;
(*[!0-9]*)
# contains non-numeric character(s)
︙
;;
(*)
# is a whole number (non-negative integer)
︙
esac
where [!0-9]
is the old-timey shell way of saying [^0-9]
,
which, of course, means any character other than a digit.
([!…]
and [^…]
both work in bash.
[!…]
is required to work by POSIX; the result of [^…]
is unspecified.)
If you don’t care which kind of non-number a string is,
you can combine the non-number patterns:
case "$1" in
("" | *[!0-9]*)
# not a number
︙
;;
(*)
# is a number
︙
esac
As an exercise,
here’s a case
statement to handle any kind of real number —
to be precise, a string of one or more digits,
with optionally a period (.
) somewhere,
and optionally a minus sign (-
) at the beginning.
case "$1" in
(*[!-.0-9]*)
# contains non-numeric character(s)
;;
(*?-*)
# contains '-' somewhere other than the first position
;;
(*.*.*)
# contains multiple decimal points
;;
(*)
case "$1" in
(*[0-9]*)
# is a real number
;;
(*)
# not a number
esac
esac
I added the case
-within-a-case
to verify that the string does, indeed,
contain at least one digit.
That wasn’t necessary in the integer example
because I tested whether the string was null;
a test which I have removed from this statement.
Without the second case
, a single -
or a single .
—
or even -.
— would qualify as a number.
Of course we could add patterns to handle those exceptions,
but that can get complex.
(For example, I almost posted this answer
without realizing that -.
was one of the exceptions.)
I believe that the above approach is more flexible and robust.
Of course the non-number patterns can be combined here, too:
(*[!-.0-9]* | *?-* | *.*.*)
.
Note that on many systems and locales[0-9]
matches more than[0123456789]
. Generally, you can't rely on ranges outside of the C locale.[[:digit:]]
should be OK though.[0123456789]
is the safest.
– Stéphane Chazelas
13 mins ago
add a comment |
To match numbers with regexp in case
statements, you'd need a shell whose wildcards support regexps. I only know of ksh93 with those.
With ksh93 globs, you can do ~(E)^[0-9]+$
or ~(E:^[0-9]+$)
to use an E
xtended regexp in a glob pattern, or ~(P)^d+$
to use a perl-like regexp (also G
for basic regexp, X
for augmented regexp, V
for SysV regexp).
So:
#! /bin/ksh93
for i do
case $i in
(~(E)^[0-9]+$)
n=$i;;
(*)
echo >&2 'Invalid argument!'
usage
esac
done
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f432660%2fmatching-numbers-with-regex-in-case-statement%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
case
does not use regexes, it uses patterns
For "1 or more digits", do this:
shopt -s extglob
...
case ${!i} in
+([[:digit:]]) )
n=${!i}
;;
...
If you want to use regular expressions, use the =~
operator within [[...]]
if [[ ${!i} =~ ^[[:digit:]]+$ ]]; then
n=${!i}
else
echo "Invalid"
fi
add a comment |
case
does not use regexes, it uses patterns
For "1 or more digits", do this:
shopt -s extglob
...
case ${!i} in
+([[:digit:]]) )
n=${!i}
;;
...
If you want to use regular expressions, use the =~
operator within [[...]]
if [[ ${!i} =~ ^[[:digit:]]+$ ]]; then
n=${!i}
else
echo "Invalid"
fi
add a comment |
case
does not use regexes, it uses patterns
For "1 or more digits", do this:
shopt -s extglob
...
case ${!i} in
+([[:digit:]]) )
n=${!i}
;;
...
If you want to use regular expressions, use the =~
operator within [[...]]
if [[ ${!i} =~ ^[[:digit:]]+$ ]]; then
n=${!i}
else
echo "Invalid"
fi
case
does not use regexes, it uses patterns
For "1 or more digits", do this:
shopt -s extglob
...
case ${!i} in
+([[:digit:]]) )
n=${!i}
;;
...
If you want to use regular expressions, use the =~
operator within [[...]]
if [[ ${!i} =~ ^[[:digit:]]+$ ]]; then
n=${!i}
else
echo "Invalid"
fi
answered Mar 21 '18 at 19:41
glenn jackmanglenn jackman
53.7k673115
53.7k673115
add a comment |
add a comment |
As glenn says, “case
does not use regexes, it uses patterns”.
As bash(1) says,
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
Acase
command first expandsword
,
and tries to match it against eachpattern
in turn,
using the same matching rules as for pathname expansion
(see Pathname Expansion below).
Similarly, the POSIX specification says,
… each pattern … shall be compared against the expansion of word,
according to the rules described in Pattern Matching Notation …
So the patterns are pathname expansion patterns,
a.k.a. wildcards, a.k.a. globs, as in ls -l -- *.sh
or rm -- *.bak
.
Sure, shopt -s extglob
and [[ … =~ … ]]
are the neatest thing since sliced bread,
but they aren’t POSIX,
and it can be useful to know how to use the original tools.
For years, programmers checked, for example,
whether a string was a number
by checking whether it was not not a number.
You’ve defined a number to be a string that consists
(entirely) of one or more digits.
So a string is not a number if it is null,
or if it contains a character that is not a digit.
We can test these conditions with a case
statement as follows:
case "$1" in
("")
# null
︙
;;
(*[!0-9]*)
# contains non-numeric character(s)
︙
;;
(*)
# is a whole number (non-negative integer)
︙
esac
where [!0-9]
is the old-timey shell way of saying [^0-9]
,
which, of course, means any character other than a digit.
([!…]
and [^…]
both work in bash.
[!…]
is required to work by POSIX; the result of [^…]
is unspecified.)
If you don’t care which kind of non-number a string is,
you can combine the non-number patterns:
case "$1" in
("" | *[!0-9]*)
# not a number
︙
;;
(*)
# is a number
︙
esac
As an exercise,
here’s a case
statement to handle any kind of real number —
to be precise, a string of one or more digits,
with optionally a period (.
) somewhere,
and optionally a minus sign (-
) at the beginning.
case "$1" in
(*[!-.0-9]*)
# contains non-numeric character(s)
;;
(*?-*)
# contains '-' somewhere other than the first position
;;
(*.*.*)
# contains multiple decimal points
;;
(*)
case "$1" in
(*[0-9]*)
# is a real number
;;
(*)
# not a number
esac
esac
I added the case
-within-a-case
to verify that the string does, indeed,
contain at least one digit.
That wasn’t necessary in the integer example
because I tested whether the string was null;
a test which I have removed from this statement.
Without the second case
, a single -
or a single .
—
or even -.
— would qualify as a number.
Of course we could add patterns to handle those exceptions,
but that can get complex.
(For example, I almost posted this answer
without realizing that -.
was one of the exceptions.)
I believe that the above approach is more flexible and robust.
Of course the non-number patterns can be combined here, too:
(*[!-.0-9]* | *?-* | *.*.*)
.
Note that on many systems and locales[0-9]
matches more than[0123456789]
. Generally, you can't rely on ranges outside of the C locale.[[:digit:]]
should be OK though.[0123456789]
is the safest.
– Stéphane Chazelas
13 mins ago
add a comment |
As glenn says, “case
does not use regexes, it uses patterns”.
As bash(1) says,
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
Acase
command first expandsword
,
and tries to match it against eachpattern
in turn,
using the same matching rules as for pathname expansion
(see Pathname Expansion below).
Similarly, the POSIX specification says,
… each pattern … shall be compared against the expansion of word,
according to the rules described in Pattern Matching Notation …
So the patterns are pathname expansion patterns,
a.k.a. wildcards, a.k.a. globs, as in ls -l -- *.sh
or rm -- *.bak
.
Sure, shopt -s extglob
and [[ … =~ … ]]
are the neatest thing since sliced bread,
but they aren’t POSIX,
and it can be useful to know how to use the original tools.
For years, programmers checked, for example,
whether a string was a number
by checking whether it was not not a number.
You’ve defined a number to be a string that consists
(entirely) of one or more digits.
So a string is not a number if it is null,
or if it contains a character that is not a digit.
We can test these conditions with a case
statement as follows:
case "$1" in
("")
# null
︙
;;
(*[!0-9]*)
# contains non-numeric character(s)
︙
;;
(*)
# is a whole number (non-negative integer)
︙
esac
where [!0-9]
is the old-timey shell way of saying [^0-9]
,
which, of course, means any character other than a digit.
([!…]
and [^…]
both work in bash.
[!…]
is required to work by POSIX; the result of [^…]
is unspecified.)
If you don’t care which kind of non-number a string is,
you can combine the non-number patterns:
case "$1" in
("" | *[!0-9]*)
# not a number
︙
;;
(*)
# is a number
︙
esac
As an exercise,
here’s a case
statement to handle any kind of real number —
to be precise, a string of one or more digits,
with optionally a period (.
) somewhere,
and optionally a minus sign (-
) at the beginning.
case "$1" in
(*[!-.0-9]*)
# contains non-numeric character(s)
;;
(*?-*)
# contains '-' somewhere other than the first position
;;
(*.*.*)
# contains multiple decimal points
;;
(*)
case "$1" in
(*[0-9]*)
# is a real number
;;
(*)
# not a number
esac
esac
I added the case
-within-a-case
to verify that the string does, indeed,
contain at least one digit.
That wasn’t necessary in the integer example
because I tested whether the string was null;
a test which I have removed from this statement.
Without the second case
, a single -
or a single .
—
or even -.
— would qualify as a number.
Of course we could add patterns to handle those exceptions,
but that can get complex.
(For example, I almost posted this answer
without realizing that -.
was one of the exceptions.)
I believe that the above approach is more flexible and robust.
Of course the non-number patterns can be combined here, too:
(*[!-.0-9]* | *?-* | *.*.*)
.
Note that on many systems and locales[0-9]
matches more than[0123456789]
. Generally, you can't rely on ranges outside of the C locale.[[:digit:]]
should be OK though.[0123456789]
is the safest.
– Stéphane Chazelas
13 mins ago
add a comment |
As glenn says, “case
does not use regexes, it uses patterns”.
As bash(1) says,
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
Acase
command first expandsword
,
and tries to match it against eachpattern
in turn,
using the same matching rules as for pathname expansion
(see Pathname Expansion below).
Similarly, the POSIX specification says,
… each pattern … shall be compared against the expansion of word,
according to the rules described in Pattern Matching Notation …
So the patterns are pathname expansion patterns,
a.k.a. wildcards, a.k.a. globs, as in ls -l -- *.sh
or rm -- *.bak
.
Sure, shopt -s extglob
and [[ … =~ … ]]
are the neatest thing since sliced bread,
but they aren’t POSIX,
and it can be useful to know how to use the original tools.
For years, programmers checked, for example,
whether a string was a number
by checking whether it was not not a number.
You’ve defined a number to be a string that consists
(entirely) of one or more digits.
So a string is not a number if it is null,
or if it contains a character that is not a digit.
We can test these conditions with a case
statement as follows:
case "$1" in
("")
# null
︙
;;
(*[!0-9]*)
# contains non-numeric character(s)
︙
;;
(*)
# is a whole number (non-negative integer)
︙
esac
where [!0-9]
is the old-timey shell way of saying [^0-9]
,
which, of course, means any character other than a digit.
([!…]
and [^…]
both work in bash.
[!…]
is required to work by POSIX; the result of [^…]
is unspecified.)
If you don’t care which kind of non-number a string is,
you can combine the non-number patterns:
case "$1" in
("" | *[!0-9]*)
# not a number
︙
;;
(*)
# is a number
︙
esac
As an exercise,
here’s a case
statement to handle any kind of real number —
to be precise, a string of one or more digits,
with optionally a period (.
) somewhere,
and optionally a minus sign (-
) at the beginning.
case "$1" in
(*[!-.0-9]*)
# contains non-numeric character(s)
;;
(*?-*)
# contains '-' somewhere other than the first position
;;
(*.*.*)
# contains multiple decimal points
;;
(*)
case "$1" in
(*[0-9]*)
# is a real number
;;
(*)
# not a number
esac
esac
I added the case
-within-a-case
to verify that the string does, indeed,
contain at least one digit.
That wasn’t necessary in the integer example
because I tested whether the string was null;
a test which I have removed from this statement.
Without the second case
, a single -
or a single .
—
or even -.
— would qualify as a number.
Of course we could add patterns to handle those exceptions,
but that can get complex.
(For example, I almost posted this answer
without realizing that -.
was one of the exceptions.)
I believe that the above approach is more flexible and robust.
Of course the non-number patterns can be combined here, too:
(*[!-.0-9]* | *?-* | *.*.*)
.
As glenn says, “case
does not use regexes, it uses patterns”.
As bash(1) says,
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac
Acase
command first expandsword
,
and tries to match it against eachpattern
in turn,
using the same matching rules as for pathname expansion
(see Pathname Expansion below).
Similarly, the POSIX specification says,
… each pattern … shall be compared against the expansion of word,
according to the rules described in Pattern Matching Notation …
So the patterns are pathname expansion patterns,
a.k.a. wildcards, a.k.a. globs, as in ls -l -- *.sh
or rm -- *.bak
.
Sure, shopt -s extglob
and [[ … =~ … ]]
are the neatest thing since sliced bread,
but they aren’t POSIX,
and it can be useful to know how to use the original tools.
For years, programmers checked, for example,
whether a string was a number
by checking whether it was not not a number.
You’ve defined a number to be a string that consists
(entirely) of one or more digits.
So a string is not a number if it is null,
or if it contains a character that is not a digit.
We can test these conditions with a case
statement as follows:
case "$1" in
("")
# null
︙
;;
(*[!0-9]*)
# contains non-numeric character(s)
︙
;;
(*)
# is a whole number (non-negative integer)
︙
esac
where [!0-9]
is the old-timey shell way of saying [^0-9]
,
which, of course, means any character other than a digit.
([!…]
and [^…]
both work in bash.
[!…]
is required to work by POSIX; the result of [^…]
is unspecified.)
If you don’t care which kind of non-number a string is,
you can combine the non-number patterns:
case "$1" in
("" | *[!0-9]*)
# not a number
︙
;;
(*)
# is a number
︙
esac
As an exercise,
here’s a case
statement to handle any kind of real number —
to be precise, a string of one or more digits,
with optionally a period (.
) somewhere,
and optionally a minus sign (-
) at the beginning.
case "$1" in
(*[!-.0-9]*)
# contains non-numeric character(s)
;;
(*?-*)
# contains '-' somewhere other than the first position
;;
(*.*.*)
# contains multiple decimal points
;;
(*)
case "$1" in
(*[0-9]*)
# is a real number
;;
(*)
# not a number
esac
esac
I added the case
-within-a-case
to verify that the string does, indeed,
contain at least one digit.
That wasn’t necessary in the integer example
because I tested whether the string was null;
a test which I have removed from this statement.
Without the second case
, a single -
or a single .
—
or even -.
— would qualify as a number.
Of course we could add patterns to handle those exceptions,
but that can get complex.
(For example, I almost posted this answer
without realizing that -.
was one of the exceptions.)
I believe that the above approach is more flexible and robust.
Of course the non-number patterns can be combined here, too:
(*[!-.0-9]* | *?-* | *.*.*)
.
edited Mar 22 '18 at 18:26
answered Mar 22 '18 at 5:09
G-ManG-Man
14.2k93973
14.2k93973
Note that on many systems and locales[0-9]
matches more than[0123456789]
. Generally, you can't rely on ranges outside of the C locale.[[:digit:]]
should be OK though.[0123456789]
is the safest.
– Stéphane Chazelas
13 mins ago
add a comment |
Note that on many systems and locales[0-9]
matches more than[0123456789]
. Generally, you can't rely on ranges outside of the C locale.[[:digit:]]
should be OK though.[0123456789]
is the safest.
– Stéphane Chazelas
13 mins ago
Note that on many systems and locales
[0-9]
matches more than [0123456789]
. Generally, you can't rely on ranges outside of the C locale. [[:digit:]]
should be OK though. [0123456789]
is the safest.– Stéphane Chazelas
13 mins ago
Note that on many systems and locales
[0-9]
matches more than [0123456789]
. Generally, you can't rely on ranges outside of the C locale. [[:digit:]]
should be OK though. [0123456789]
is the safest.– Stéphane Chazelas
13 mins ago
add a comment |
To match numbers with regexp in case
statements, you'd need a shell whose wildcards support regexps. I only know of ksh93 with those.
With ksh93 globs, you can do ~(E)^[0-9]+$
or ~(E:^[0-9]+$)
to use an E
xtended regexp in a glob pattern, or ~(P)^d+$
to use a perl-like regexp (also G
for basic regexp, X
for augmented regexp, V
for SysV regexp).
So:
#! /bin/ksh93
for i do
case $i in
(~(E)^[0-9]+$)
n=$i;;
(*)
echo >&2 'Invalid argument!'
usage
esac
done
add a comment |
To match numbers with regexp in case
statements, you'd need a shell whose wildcards support regexps. I only know of ksh93 with those.
With ksh93 globs, you can do ~(E)^[0-9]+$
or ~(E:^[0-9]+$)
to use an E
xtended regexp in a glob pattern, or ~(P)^d+$
to use a perl-like regexp (also G
for basic regexp, X
for augmented regexp, V
for SysV regexp).
So:
#! /bin/ksh93
for i do
case $i in
(~(E)^[0-9]+$)
n=$i;;
(*)
echo >&2 'Invalid argument!'
usage
esac
done
add a comment |
To match numbers with regexp in case
statements, you'd need a shell whose wildcards support regexps. I only know of ksh93 with those.
With ksh93 globs, you can do ~(E)^[0-9]+$
or ~(E:^[0-9]+$)
to use an E
xtended regexp in a glob pattern, or ~(P)^d+$
to use a perl-like regexp (also G
for basic regexp, X
for augmented regexp, V
for SysV regexp).
So:
#! /bin/ksh93
for i do
case $i in
(~(E)^[0-9]+$)
n=$i;;
(*)
echo >&2 'Invalid argument!'
usage
esac
done
To match numbers with regexp in case
statements, you'd need a shell whose wildcards support regexps. I only know of ksh93 with those.
With ksh93 globs, you can do ~(E)^[0-9]+$
or ~(E:^[0-9]+$)
to use an E
xtended regexp in a glob pattern, or ~(P)^d+$
to use a perl-like regexp (also G
for basic regexp, X
for augmented regexp, V
for SysV regexp).
So:
#! /bin/ksh93
for i do
case $i in
(~(E)^[0-9]+$)
n=$i;;
(*)
echo >&2 'Invalid argument!'
usage
esac
done
answered Mar 22 '18 at 7:30
Stéphane ChazelasStéphane Chazelas
318k57602966
318k57602966
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f432660%2fmatching-numbers-with-regex-in-case-statement%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
This variable indirection works just fine. I have more cases in the real script and it works. I'm trying to match any occurrence of real numbers in the program arguments. So 0 or 999 should match. Else if there is some invalid argument like '-x' or letters in stead of numbers, program shall match
*
, at least thats what I thought.– siery
Mar 21 '18 at 19:35
@John1024, numbers aren't valid names for variables, but they're quite valid for the names of the positional parameters, and
${!i}
works fine for those. e.g.set -- aa bb cc; i=2; echo ${!i}
printsbb
– ilkkachu
Mar 21 '18 at 22:45
that said, the easier way to loop over the arguments to the script would be to just use
for val in "$@"; do ...
and use$val
in the loop– ilkkachu
Mar 21 '18 at 22:46
Read unix.stackexchange.com/questions/119905/…
– Gilles
Mar 21 '18 at 22:56
@John1024, and when it's run,
i
contains1
, so${!i}
is the same as$1
: it expands to the value of the first argument, be it64
orabc
or whatever. What they have is just a convoluted way of looping over the positional parameters / command line arguments.– ilkkachu
Mar 21 '18 at 23:14