2

summary: I'd like to use a bash case statement (in other code) to classify inputs as to whether they are

  • a positive integer
  • a negative integer
  • zero
  • an empty string
  • a non-integer string

Executable code follows, which is correctly classifying the following inputs:

  • ''
  • word
  • a\nmultiline\nstring
  • 2.1
  • -3

but is classifying both the following as ... negative integers :-(

  • 0
  • 42

details:

Save the following to a file (e.g. /tmp/integer_case_statement.sh), chmod it, and run it:

#!/usr/bin/env bash

### This should be a simple `bash` `case` statement to classify inputs as
### {positive|negative|zero|non-} integers.
### Trying extglob, since my previous integer-match patterns failed.
### Gotta turn that on before *function definition* per https://stackoverflow.com/a/34913651/915044
shopt -s extglob

declare cmd=''

function identify() {
    local -r it=${1}  # no quotes in case it's multiline
#    shopt -s extglob # can't do that here
    case ${it} in
        '')
            # empty string, no need for `if [[ -z ...`
            >&2 echo 'ERROR: null arg'
            ;;
        ?(-|+)+([[:digit:]]))
            # it's an integer, so just say so and fallthrough
            >&2 echo 'DEBUG: int(it), fallthrough'
            ;&
        -+([[:digit:]]))
            # it's negative: just return it
            >&2 echo 'DEBUG: int(it) && (it < 0), returning it'
            echo "${it}"
            ;;
        0)
            # it's zero: that's OK
            >&2 echo 'DEBUG: int(it) && (it == 0), returning it'
            echo '0'
            ;;
        ++([[:digit:]]))
            # it's positive: just return it
            >&2 echo 'DEBUG: int(it) && (it > 0), returning it'
            echo "${it}"
            ;;
        *)
            # not an integer, just return it
            >&2 echo 'DEBUG: !int(it)'
            echo "${it}"
            ;;
    esac
} # end function identify

echo -e "'bash --version'==${BASH_VERSION}\n"

echo "identify '':"
identify ''
echo
# > ERROR: null arg

echo 'identify word:'
identify word
echo
# > DEBUG: !int(it)
# > word

echo 'identify a
multiline
string:'
identify 'a
multiline
string'
echo
# > DEBUG: !int(it)
# > a
# > multiline
# > string

echo 'identify 2.1:'
identify 2.1
echo
# > DEBUG: !int(it)
# > 2.1

echo 'identify -3:'
identify -3
echo
# > DEBUG: int(it), fallthrough
# > DEBUG: int(it) && (it < 0), returning it
# > -3

echo 'identify 0:'
identify 0
echo
# > DEBUG: int(it), fallthrough
# > DEBUG: int(it) && (it < 0), returning it
# > 0

echo 'identify 42:'
identify 42
echo
# > DEBUG: int(it), fallthrough
# > DEBUG: int(it) && (it < 0), returning it
# > 42

exit 0

The current output is inlined in the file, but for ease of reading, here's my current output separately:

'bash --version'==4.3.30(1)-release

identify '':
ERROR: null arg

identify word:
DEBUG: !int(it)
word

identify a
multiline
string:
DEBUG: !int(it)
a
multiline
string

identify 2.1:
DEBUG: !int(it)
2.1

identify -3:
DEBUG: int(it), fallthrough
DEBUG: int(it) && (it < 0), returning it
-3

identify 0:
DEBUG: int(it), fallthrough
DEBUG: int(it) && (it < 0), returning it
0

identify 42:
DEBUG: int(it), fallthrough
DEBUG: int(it) && (it < 0), returning it
42

The latter 2 inputs are my problem: why is the case statement identifying

  • 0 as a negative integer (rather than as 0)
  • 42 as a negative integer (rather than as positive)

? Your assistance is appreciated.

3
  • 2
    ;;& (test the other cases fallthrough) instead of ;& (run next case code fallthrough) Jun 17 '19 at 7:47
  • 1
    ... and make the + optional for positive integers to catch 42 or +42: ?(+)+([[:digit:]]))
    – Freddy
    Jun 17 '19 at 8:18
  • @frostschutz: You should post that the answer
    – Inian
    Jun 17 '19 at 8:24
1

summary: Thanks to

  • frostschutz for s/;&/;;&/
  • Freddy for the correct positive-integer pattern (guess I just got nightblind)

I also added an additional clause to detect signed zeros, and a few more testcases.

details:

Save this improved code to a file (e.g. /tmp/integer_case_statement.sh), chmod it, and run it:

#!/usr/bin/env bash

### Simple `bash` `case` statement to classify inputs as {positive|negative|zero|non-} integers.
### Trying extglob, since my previous integer-match patterns failed.
### Gotta turn that on before *function definition* per https://stackoverflow.com/a/34913651/915044
shopt -s extglob

declare input=''

### For `case` *patterns* (NOT regexps), see
### https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html
function identify() {
    local -r it=${1}  # no quotes in case it's multiline
#    shopt -s extglob # can't do that here
    case ${it} in
        '')
            # empty string, no need for `if [[ -z ...`
            >&2 echo 'ERROR: null arg'
            ;;
        [+-]0)
            >&2 echo 'ERROR: zero should not be signed'
            ;;
        ?(-|+)+([[:digit:]]))
            # it's an integer, so just say so and fallthrough
            >&2 echo 'DEBUG: int(it), fallthrough'
#            ;& # this only runs the next clause, thanks https://unix.stackexchange.com/users/30851/frostschutz
            ;;& # works
        -+([[:digit:]]))
            >&2 echo 'DEBUG: it < 0'
            ;;
        0)
            >&2 echo 'DEBUG: it == 0'
            echo '0'
            ;;
        ?(+)+([[:digit:]])) # thanks https://unix.stackexchange.com/users/332764/freddy
            >&2 echo 'DEBUG: it > 0'
            ;;
        *)
            >&2 echo 'DEBUG: !int(it)'
            ;;
    esac
} # end function identify

echo -e "'bash --version'==${BASH_VERSION}\n"

for input in \
    '' \
    '@#$%^&!' \
    'word' \
    'a
multiline
string' \
    '2.1' \
    '-3' \
    '+3' \
    '+0' \
    '0' \
    '-0' \
    '42' \
; do
    echo "identify '${input}'"
    identify "${input}"
    ret_val="${?}"
    if [[ "${ret_val}" -ne 0 ]] ; then
        >&2 echo "ERROR: retval='${ret_val}', exiting ..."
        exit 3
    fi
    echo # newline
done

exit 0

On this Debian workstation, the above currently outputs:

'bash --version'==4.3.30(1)-release

identify ''
ERROR: null arg

identify '@#$%^&!'
DEBUG: !int(it)

identify 'word'
DEBUG: !int(it)

identify 'a
multiline
string'
DEBUG: !int(it)

identify '2.1'
DEBUG: !int(it)

identify '-3'
DEBUG: int(it), fallthrough
DEBUG: it < 0

identify '+3'
DEBUG: int(it), fallthrough
DEBUG: it > 0

identify '+0'
ERROR: zero should not be signed

identify '0'
DEBUG: int(it), fallthrough
DEBUG: it == 0
0

identify '-0'
ERROR: zero should not be signed

identify '42'
DEBUG: int(it), fallthrough
DEBUG: it > 0

Your assistance is appreciated!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.