BASH mag meinen regulären Ausdruck nicht

BASH mag meinen regulären Ausdruck nicht

Ich versuche, den zweistelligen Monat und das zweistellige Jahr der Änderung einer Datei herauszufinden, aber es funktioniert nicht.

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

Was mache ich falsch?

Antwort1

Erstens unterdrücken die Anführungszeichen die Bedeutung der Sonderzeichen im regulären Ausdruck (Online-Handbuch):

Ein zusätzlicher binärer Operator, =~, ist verfügbar, … Jeder Teil des Musters kann in Anführungszeichen gesetzt werden, um die Übereinstimmung mit dem zitierten Teil als Zeichenfolge zu erzwingen. … Wenn Sie ein Zeichen zuordnen möchten, das für die Grammatik regulärer Ausdrücke eine Sonderstellung hat, muss es in Anführungszeichen gesetzt werden, um seine besondere Bedeutung zu entfernen.

Das Handbuch empfiehlt weiterhin, den regulären Ausdruck in eine Variable einzufügen, um Konflikte zwischen der Shell-Analyse und der Regex-Syntax zu vermeiden.

Zweitens: \dEs tut nicht das, was Sie denken, sondern gleicht nur einen wörtlichen Wert ab d.

Beachten Sie auch, dass ${BASH_REMATCH[0]}die gesamte übereinstimmende Zeichenfolge enthalten ist und die Indizes 1und höher die erfassten Gruppen enthalten.

Ich würde außerdem dringend empfehlen, vierstellige Jahreszahlen zu verwenden, also:

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

Für eine heute geänderte Datei ergibt das year: 2018und month: 08. Beachten Sie, dass Zahlen mit einer führenden Null von der Shell und möglicherweise anderen Dienstprogrammen als Oktalzahlen betrachtet werden.

(Vierstellige Jahreszahlen bereiten weniger Probleme, wenn Sie jemals mit Daten aus den 1900er Jahren arbeiten müssen, und sie sind leichter als Jahre und nicht als Monatstage zu erkennen.)

Antwort2

Hierfür sind keine regulären Ausdrücke erforderlich:

$ 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

Antwort3

Alternativ datekönnen Sie mit GNU Folgendes tun:

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

Um die Jahres-, Monats- und Tageskomponenten der Änderungszeit jeweils in und $yearzu speichern (entfernen Sie als Dezimalzahlen das s in und, wenn Ihnen die führenden Nullen nicht egal sind; siehe auch für zweistellige Jahreszahlen).$month$day-%-m%-d%y

(Beachten Sie, dass im Gegensatz zu GNU statbei Dateien des Typs „symlink“ der Änderungszeitpunkt des Ziels des symbolischen Links und nicht der des symbolischen Links selbst berücksichtigt wird. Bei GNU statwürden Sie verwenden stat -L.)

verwandte Informationen