BASH não gosta do meu regex

BASH não gosta do meu regex

Estou tentando obter o mês de 2 dígitos e o ano de 2 dígitos em 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?

Responder1

Primeiro, as aspas suprimem o significado dos caracteres especiais na regex (manual on-line):

Um operador binário adicional, =~, está disponível, ... Qualquer parte do padrão pode ser citada para forçar a correspondência da parte citada como uma string. ... Se você deseja corresponder um caractere especial à gramática da expressão regular, ele deve ser colocado entre aspas para remover seu significado especial.

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

Segundo, \dnão faz o que você pensa, mas apenas corresponde a um literal d.

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

Eu também sugiro fortemente o uso de 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: 2018e month: 08. Observe que os números com zero à esquerda serão considerados octais pelo shell e possivelmente por outros utilitários.

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

Responder2

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

Responder3

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

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

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

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

informação relacionada