A BASH no le gusta mi expresión regular

A BASH no le gusta mi expresión regular

Estoy intentando obtener el mes de 2 dígitos y el año de 2 dígitos en que se modificó el archivo, pero no funciona.

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

¿Qué estoy haciendo mal?

Respuesta1

Primero, las comillas suprimen el significado de los caracteres especiales en la expresión regular (Manual en Linea):

Un operador binario adicional, =~, está disponible, ... Cualquier parte del patrón puede entrecomillarse para forzar que la parte citada coincida como una cadena. ... Si desea hacer coincidir un carácter que es especial con la gramática de la expresión regular, debe citarse para eliminar su significado especial.

El manual continúa recomendando poner la expresión regular en una variable para evitar algunos conflictos entre el análisis del shell y la sintaxis de la expresión regular.

En segundo lugar, \dno hace lo que crees que hace, sino que simplemente coincide con un literal d.

También tenga en cuenta que ${BASH_REMATCH[0]}contiene toda la cadena coincidente, y que los índices 1y superiores contienen los grupos capturados.

También recomiendo encarecidamente utilizar años de cuatro dígitos, por lo que:

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 un archivo modificado hoy, eso da year: 2018y month: 08. Tenga en cuenta que los números con un cero a la izquierda serán considerados octales por el shell y posiblemente por otras utilidades.

(Los años de cuatro dígitos tienen menos problemas si alguna vez necesitas manejar fechas de 1900, y son más fáciles de reconocer como años y no como días del mes).

Respuesta2

No necesito expresiones regulares para esto:

$ 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

Respuesta3

Como alternativa, con GNU date, puedes hacer:

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

Para tener el componente de año, mes y día de la hora de modificación almacenado en $yeary $monthrespectivamente $day(como números enteros decimales, elimine la -s en %-my %-dsi le interesan los ceros iniciales; consulte también %ypara años de 2 dígitos).

(tenga en cuenta que, a diferencia de GNU stat, para archivos de tipo enlace simbólico, se considera el tiempo de modificación del destino del enlace simbólico en lugar del del enlace simbólico en sí. Con GNU stat, usaría stat -L).

información relacionada