BASH não gosta da minha regex

4

Estou tentando obter o mês de dois dígitos e o ano de dois dígitos que um arquivo foi modificado, mas não está funcionando ..

modified=$(stat -c %y "$line"); 
# modified="2018-08-22 14:39:36.400469308 -0400"
if [[ $modified =~ ".{2}(\d{2})-(\d{2})" ]]; then
    echo ${BASH_REMATCH[0]}
    echo ${BASH_REMATCH[1]
fi

O que estou fazendo de errado?

    
por Occam's Razor 27.08.2018 / 22:59

3 respostas

7

Primeiro, as aspas suprimem o significado dos caracteres especiais no regex ( manual online ):

An additional binary operator, =~, is available, ... Any part of the pattern may be quoted to force the quoted portion to be matched as a string. ... If you want to match a character that’s special to the regular expression grammar, it has to be quoted to remove its special meaning.

O manual continua recomendando colocar o regex em uma variável para evitar alguns conflitos entre a análise do shell e a sintaxe regex.

Em segundo lugar, \d não faz o que você acha que faz, mas apenas corresponde a um literal d .

Observe também que ${BASH_REMATCH[0]} contém toda a string correspondente, e os índices 1 e up contêm os grupos capturados.

Eu também sugiro strongmente usar anos de quatro dígitos, então:

modified=$(stat -c %y "$file")
re='^([0-9]{4})-([0-9]{2})'
if [[ $modified =~ $re ]]; then
    echo "year:  ${BASH_REMATCH[1]}"
    echo "month: ${BASH_REMATCH[2]}"
else
    echo "invalid timestamp"
fi

Para um arquivo modificado hoje, isso dá year: 2018 e month: 08 . Observe que os números com um zero à esquerda serão considerados octal pelo shell e possivelmente por outros utilitários.

(Anos de quatro dígitos têm menos problemas se você precisar lidar com datas de 1900, e eles são mais fáceis de reconhecer como anos e não como dias do mês.)

    
por 27.08.2018 / 23:21
3

Não precisa de expressões regulares para isso:

$ touch -t 197001010000 myfile
$ ls -l myfile
-rw-rw-r-- 1 jackman jackman 0 Jan  1  1970 myfile
$ IFS='-' read -r year month _rest < <(stat -c %y myfile)
$ echo "$year:${year#??}"$month"
1970:70:01
    
por 27.08.2018 / 23:25
3

Como alternativa, com o GNU date , você pode fazer:

eval "$(date -r "$file" +'year=%Y month=%-m day=%-d')"

Para que o componente de ano, mês e dia da hora da modificação seja armazenado em $year , $month e $day respectivamente (como números inteiros decimais, remova - s em %-m e %-d se você se importar para os zeros à esquerda; veja também %y para os anos com 2 dígitos).

(note que ao contrário do GNU stat , para arquivos do tipo symlink, o tempo de modificação do destino do link simbólico é considerado ao invés do próprio link simbólico. Com o GNU stat , você usaria stat -L ).

    
por 27.08.2018 / 23:33