i want to test if a variable has more than 4 digits something like this
#!/bin/bash
if [ $input has more than 4 digits ]; then
echo " * Please only 4 digits" >&2
echo""
else
the other option
fi
i want to test if a variable has more than 4 digits something like this
#!/bin/bash
if [ $input has more than 4 digits ]; then
echo " * Please only 4 digits" >&2
echo""
else
the other option
fi
If you care about the number of digits (and not the numerical value), you could match against a regex in Bash/Ksh/Zsh (* see footnote on [[:digit:]]
):
#!/bin/bash
input=$1
re='^[[:digit:]]{1,4}$'
if [[ $input =~ $re ]]; then
echo "'$input' contains 1 to 4 digits (and nothing else)"
else
echo "'$input' contains something else"
fi
Or e.g. [[ $input =~ ^[[:digit:]]{5,}$ ]]
to check for "5 or more digits (and nothing else)", etc.
Or in a pure POSIX shell, where you have to use case
for the pattern match:
#!/bin/sh
input=$1
case $input in
*[![:digit:]]*) onlydigits=0;; # contains non-digits
*[[:digit:]]*) onlydigits=1;; # at least one digit
*) onlydigits=0;; # empty
esac
if [ $onlydigits = 0 ]; then
echo "'$input' is empty or contains something other than digits"
elif [ "${#input}" -le 4 ]; then
echo "'$input' contains 1 to 4 digits (and nothing else)"
else
echo "'$input' contains 5 or more digits (but nothing else)"
fi
(You could put all the logic inside the case
, but nesting an if
there is somewhat ugly, IMO.)
Note that [[:digit:]]
should match whatever the current locale's idea of "digits" is. That might or might not be more than the ASCII digits 0123456789
. On my system, [[:digit:]]
does not match e.g. ⁴ (superscript four, U+2074), but [0-9]
does. Matching other "digits" might be a problem, esp. if you do arithmetic on the number in the shell. So, if you want to be stricter, use [0123456789]
to accept just the ASCII digits.
[0-9]
, or [0123456789]
or [[:alpha:]]
or whatever.
– ilkkachu
Aug 11 '21 at 11:54
Here assuming you mean ASCII decimal digits only and not other sorts of decimal or non-decimal digits.
shopt -s extglob # enables a subset of ksh extended globs including *(...),
# +(...) and ?(...) but unfortunately not {4}(...)
d='[0123456789]' nd='[^0123456789]'
case $input in
( $d$d$d$d+($d) ) echo made of more than 4 digits;;
( $d$d$d$d$d ) echo contains more than 4 digits;;
( "" ) echo empty;;
( ($nd) ) echo does not contain any digit;;
( $nd* ) echo no more than 4 digits but also contains non-digits;;
( $d?($d)?($d)?($d) ) echo made of 1 to 4 digits;;
( * ) echo should not be reached;;
esac
Beware that in bash
and depending on the system and locale, [0-9]
and [[:digit:]]
may match a lot more than just 0123456789 so those should not be used for input validation (more on that in that answer to a different question here for instance).
Also beware that bash
pattern matching works in very-surprising ways in multi-byte locales.
You'll find that for instance in a zh_CN.gb18030
Chinese locale, on input='1-©©'
it will return no more than 4 digits but also contains non-digits
as expected, but if you append a single 0x80
byte (input='1-©©'$'\x80'
), it will return contains more than 4 digits
.
It's for this kind of reason (and the fact that pattern matching has been known to have bugs in corner cases in many shells) that for input validation, it's better to use a positive matching where possible for the things you accept (rather than negative match for the things to reject)¹ hence the $d?($d)?($d)?($d)
above even though it shouldn't be necessary as in theory at least, anything else should have been matched by earlier patterns.
¹ as an exception to that, one may need to consider the Bourne and Korn shell's misfeature whereby case $input in [x]) echo yes; esac
matches on x
but also on [x]
!
I'd do
#!/usr/bin/env bash
die () { echo "$*" >&2; exit 1; }
input=$1
[[ $input == +([[:digit:]]) ]] || die "only digits please"
(( input <= 9999 )) || die "no more than 4 digits please"
echo "ok: $input"
0789
will emit errors
– glenn jackman
Apr 09 '21 at 15:52
10#$input
in the arithmetic expression. Simpler to check string length indeed.
– glenn jackman
Apr 09 '21 at 16:01
If you want to examine the number of characters a variable, you can do this...
var="foo bar"
echo "var contains ${#var} characters"
Result:
var contains 7 characters
bash
(and most other shells), ${#var}
includes the number of characters but also the number of bytes not forming valid characters. For instance, in a en_US.UTF-8
locale, input=$'\xc3\xa9\x80' bash -c 'echo "${#input}"'
ouputs 2
(1 é
character made of two bytes, plus one 0x80 byte).
– Stéphane Chazelas
Apr 10 '21 at 09:49
This is another way:
#!/bin/bash
if test -z "$1"
then
echo no digit supplied
elif grep -qE '[[:digit:]]{5}' <<< "$1"
then
echo too many digits supplied
else
echo number of digits ok
fi
abc
too... But then yeah, we don't know what the asker wanted to do with inputs like that.
– ilkkachu
Aug 11 '21 at 11:29
input='foo 1 bar 2 baz 3'
has 3 ASCII decimal digits and 8 hexadecimal digits. Should it be accepted? Do you want to consider only ASCII decimal digits (0123456789), or other kinds of decimal or non-decimal digits, like ¹, ², or decimal digits in other numeral systems (꧳, ꤁...)? – Stéphane Chazelas Apr 10 '21 at 05:52