Números correspondentes com regex na instrução case

1

Eu tento algumas maneiras deferentes com as quais eu surjo (como [0-9]+ ou ^[0-9][0-9]*$ ), e nenhuma delas funciona, em uma declaração está combinando a próxima condição de caso, * .

i=1
let arg_n=$#+1

while (( $i < $arg_n )); do
    case ${!i} in
    [0-9]+)
        n=${!i}
        ;;
    *)
        echo 'Invalid argument!'
        usage
        ;;
    esac
    let i=$i+1
done

Saída:

$ ./cmd.sh 64
Invalid argument!
Usage: ...
    
por siery 21.03.2018 / 20:21

3 respostas

8

case não usa expressões regulares, usa padrões

Para "1 ou mais dígitos", faça o seguinte:

shopt -s extglob
...
    case ${!i} in
        +([[:digit:]]) )
            n=${!i}
            ;;
    ...

Se você quiser usar expressões regulares, use o operador =~ em [[...]]

if [[ ${!i} =~ ^[[:digit:]]+$ ]]; then
    n=${!i}
else
    echo "Invalid"
fi
    
por 21.03.2018 / 20:41
1

Para corresponder números com regexp em case instruções , você precisaria de um shell cujos curingas suportam regexps. Eu só sei de ksh93 com aqueles.

Com ksh93 globs, você pode usar ~(E)^[0-9]+$ ou ~(E:^[0-9]+$) para usar um regexp E xtended em um padrão glob ou ~(P)^\d+$ para usar um regexp semelhante a um perl (também G para regexp básico, X para regexp aumentada, V para regexp SysV).

Então:

#! /bin/ksh93
for i do
  case $i in
    (~(E)^[0-9]+$)
      n=$i;;
    (*)
      echo >&2 'Invalid argument!'
      usage
  esac
done
    
por 22.03.2018 / 08:30
1

Como diz o glenn , “ case não usa expressões regulares, usa patterns ". Como bash (1) diz,

case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac

        A case command first expands word, and tries to match it against each pattern in turn, using the same matching rules as for pathname expansion (see Pathname Expansion below).

Da mesma forma, a especificação POSIX diz,

… each pattern … shall be compared against the expansion of word, according to the rules described in Pattern Matching Notation …

Assim, os padrões são padrões de expansão de nome de caminho, wildcards, a.k.a. globs, como em ls -l -- *.sh ou rm -- *.bak .

Claro, shopt -s extglob e [[ … =~ … ]] são a coisa mais legal desde o pão fatiado, mas eles não são POSIX, e pode ser útil saber como usar as ferramentas originais. Durante anos, os programadores verificaram, por exemplo, se uma string era um número verificando se não foi não um número. Você definiu um número para ser uma string que consiste (inteiramente) de um ou mais dígitos. Então, uma string não é um número se for nula, ou se contiver um caractere que não seja um dígito. Podemos testar essas condições com uma instrução case da seguinte forma:

case "$1" in
    ("")
        # null
           ︙
        ;;
    (*[!0-9]*)
        # contains non-numeric character(s)
           ︙
        ;;
    (*)
        # is a whole number (non-negative integer)
           ︙
esac

onde [!0-9] é a maneira antiga de dizer [^0-9] , o que, obviamente, significa qualquer caractere que não seja um dígito. ( [!…] e [^…] trabalham no bash. [!…] é necessário para trabalhar com POSIX; o resultado de [^…] não é especificado.) Se você não se importa com o tipo de string que não é de um número, você pode combinar os padrões não numéricos:

case "$1" in
    ("" | *[!0-9]*)
        # not a number
           ︙
        ;;
    (*)
        # is a number
           ︙
esac

Como exercício, aqui está uma declaração case para lidar com qualquer tipo de número real - para ser preciso, uma seqüência de um ou mais dígitos, com opcionalmente um período ( . ) em algum lugar, e, opcionalmente, um sinal de menos ( - ) no começo.

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

Eu adicionei o case -within-a- case para verificar se a string, na verdade, conter pelo menos um dígito. Isso não foi necessário no exemplo inteiro porque eu testei se a string era nula; um teste que eu removi desta declaração. Sem o segundo case , um único - ou um único . - ou mesmo -. - qualificaria como um número. Claro que poderíamos adicionar padrões para lidar com essas exceções, mas isso pode ficar complexo. (Por exemplo, eu quase postei essa resposta sem perceber que -. foi uma das exceções.) Acredito que a abordagem acima é mais flexível e robusta.

É claro que os padrões não numéricos podem ser combinados aqui também: (*[!-.0-9]* | *?-* | *.*.*) .

    
por 22.03.2018 / 06:09