Unix Case RegEx

4

Estou tentando corresponder o padrão de data usando o comando case , mas não está funcionando. Alguém pode me ajudar?

Entrada : 01/05/1900 ou 5/6/1900

Declaração de caso usada:

ptn="[0-9]|[0-9][0-9]/[0-9]|[0-9][0-9]/[0-9][0-9]|[0-9][0-9][0-9][0-9]"

case "$dt" in 
    $ptn ) echo "valid pattern" ;;
    *) echo "invalid"
esac

Mas o padrão usado na declaração de caso não está avaliando as duas entradas de data acima.

    
por AngiSen 18.09.2016 / 14:35

4 respostas

4

Tente isso. case apenas entende globs, portanto, manipular um regexp não é simples. O operador =~ oferece algumas soluções aqui.

#!/bin/bash
INP="01/05/1900"
ptn="^([0-9]|[0-9][0-9])/([0-9]|[0-9][0-9])/([0-9][0-9]|[0-9][0-9][0-9][0-9])$"
if [[ $INP =~ $ptn ]] ; then
    echo valid pattern
else
    echo invalid
fi

Se você gosta de reduzir o tamanho do regexp, ^[0-9]{1,2}/[0-9]{1,2}/([0-9]{2}|[0-9]{4})$ funciona igualmente bem também.

    
por 18.09.2016 / 15:16
4

Como @Steve já mencionado , case precisa de uma glob, não uma expressão regular. Você ainda pode usar case , você só precisa dar globs em vez disso:

#!/bin/sh
dt="$1";
ptn1="[0-9]/[0-9]/[0-9][0-9][0-9][0-9]"           ## N/N/NNNN
ptn2="[0-9][0-9]/[0-9]/[0-9][0-9][0-9][0-9]"      ## NN/N/NNNN
ptn3="[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]"      ## N/NN/NNNN
ptn4="[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]" ## NN/NN/NNNN

case "$dt" in 
    $ptn1|$pt2|$pt3|$pt4 ) echo "$dt : valid pattern" ;;
    *) echo "$dt : invalid" ;;
esac

Em seguida, execute o script assim:

foo.sh 15/6/1900

Por exemplo:

$ for i in 05/6/1900 5/06/1900 05/06/1900 05/06/19 123/123/123; do foo.sh $i; done
05/6/1900 : valid pattern
5/06/1900 : valid pattern
05/06/1900 : valid pattern
05/06/19 : invalid
123/123/123 : invalid

Se você tiver acesso a ferramentas GNU (especificamente, GNU date ), você também pode usar um truque como:

#!/bin/sh
dt="$1";
date -d $dt &> /dev/null &&
        echo "$dt : Valid pattern" ||
                echo "$dt :Invalid pattern"

Em seguida, na mesma entrada de teste de antes:

$ for i in 05/6/1900 5/06/1900 05/06/1900 05/06/19 123/123/123; do foo.sh $i; done
05/6/1900 : Valid pattern
5/06/1900 : Valid pattern
05/06/1900 : Valid pattern
05/06/19 : Valid pattern
123/123/123 :Invalid pattern

Como você pode ver, dessa forma você pode obter padrões mais válidos mesmo para casos em que, por exemplo, o ano é dado como YY em vez de YYYY .

    
por 18.09.2016 / 15:53
3

O "padrão" que você apresenta:

ptn="[0-9]|[0-9][0-9]/[0-9]|[0-9][0-9]/[0-9][0-9]|[0-9][0-9][0-9][0-9]"

Padrão básico

Não funcionará em um padrão básico usado por maiúsculas e minúsculas. Padrões básicos não permitem | . Nem permitir o uso de contagens {a,b} (chamado "ligado" no homem 7 regex). Para fazê-lo funcionar em uma instrução case , você precisa criar explicitamente cada padrão:

ptn1="[0-9]/[0-9]/[0-9][0-9]"                       # n  / n  / nn
ptn2="[0-9]/[0-9]/[0-9][0-9][0-9][0-9]"             # n  / n  / nnnn
ptn3="[0-9]/[0-9][0-9]/[0-9][0-9]"                  # n  / nn / nn
ptn4="[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]"        # n  / nn / nnnn
ptn5="[0-9][0-9]/[0-9]/[0-9][0-9]"                  # nn / n  / nn
ptn6="[0-9][0-9]/[0-9]/[0-9][0-9][0-9][0-9]"        # nn / n  / nnnn
ptn7="[0-9][0-9]/[0-9][0-9]/[0-9][0-9]"             # nn / nn / nn
ptn8="[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]"   # nn / nn / nnnn

E, em seguida, use-o como este:

case $dt in 
    $ptn1|$ptn2|$ptn3|$ptn4|$ptn5|$ptn6|$ptn7|$ptn8 )
        echo "$dt : valid pattern" ;;
    *)  
        echo "$dt : invalid" ;;
esac

(shell) Padrão estendido.

Algumas camadas permitem o uso de padrões estendidos.
Chamada pattern-list em ksh e Extended pattern (extglob) no bash.

#!/usr/bin/ksh
ptn="{2}({1,2}([0-9])/){2,4}([0-9])" 
while read dt; do
    printf 'line tested %20s ' "$dt"
    [[ $dt == $ptn ]] && echo "valid pattern" ||  echo "invalid"
done <"infile"

Regex

Mas o padrão apresentado pode ser reduzido a esse regex mais simples (BRE):

([0-9]{1,2}/){2}[0-9]{2,4}

Qual é:

  • um ou dois dígitos seguido por um / : [0-9] {1,2} /
  • repita o acima duas vezes: () {2}
  • acrescente de dois a quatro dígitos: [0-9] {2,4}

#!/bin/bash
# also works in ksh and zsh.
reg="^([0-9]{1,2}/){2}[0-9]{2,4}$"

while read dt; do
    printf 'line tested %20s ' "$dt"
    [[ $dt =~ $reg ]] && echo "valid pattern" ||  echo "invalid"
done <"infile"

BRE regex.

Se você não pode usar ksh, bash ou zsh, então backquote o regex:

reg="^\([0-9]\{1,2\}/\)\{2\}[0-9]\{2,4\}$"

E use expr (por exemplo, sed e awk também podem ser usados):

reg="^\([0-9]\{1,2\}/\)\{2\}[0-9]\{2,4\}$"

while read dt; do
    printf 'line tested %20s ' "$dt"
    expr "$dt" : "\($reg\)" >/dev/null && echo "valid pattern" ||  echo "invalid"
done <"infile"
    
por 18.09.2016 / 23:09
0

Eu gosto de terdon answer.

Com relação à parte Regex desta pergunta, seu padrão não é válido. você deve escapar com um \ all caracteres especiais, como este:

([0-9]|[0-9][0-9])\/([0-9]|[0-9][0-9])\/([0-9][0-9]|[0-9][0-9][0-9][0-9])

Para conjuntos de dados conhecidos como datas, não tente reinventar a roda, pois há literalmente milhares de respostas na internet para datas correspondentes. Se você quiser criar algo mais pessoal, use ajudantes de regex como o meu favorito pessoal regex.com para testar e aprimorar seu padrão como desejar.

Editado: Com os caracteres de escape, o seu código irá capturar algumas datas, mas ainda assim não é perfeito, porque nunca irá captar 01/01/2016 , em vez disso irá capturar apenas 01/01/20 . A "prioridade" é de 2 números por ano, em vez de 4. Você precisa trocá-los para encontrar primeiro o maior jogo e se ele falhar, ele pegará o menor:

([0-9]|[0-9][0-9])\/([0-9]|[0-9][0-9])\/([0-9][0-9][0-9][0-9]|[0-9][0-9])

    
por 18.09.2016 / 18:29