Wie extrahiere ich bestimmte Elemente aus einem Dateinamen?

7

Ich habe eine Reihe von Dateien im folgenden Format:

2014-11-19.8.ext
2014-11-26.1.ext
2014-11-26.2.blah.ext
2014-11-26_3.ext
2014-11-26.4.stuff_here.ext
2014-12-03.1. could be anything.ext
2014-12-032b.ext
2014-11-26 613 adva.ext

Mein Ziel ist es, die gesamte Liste der Dateien zu durchlaufen und die Datumsformatierung zu übernehmen YYYY-MM-DDund diese in einer Variablen im Format YYYYMMDDfür die weitere Verarbeitung zu speichern (in meinem Fall wird sie in einen touchBefehl verschoben ).

Normalerweise würde ich also gegen diesen regulären Ausdruck antreten: (\d{4})-(\d{2})-(\d{2}).*

Und dann benutze $1$2$3, um mein gewünschtes Muster zu erhalten, aber ich bin mir nicht sicher, wie ich das in bash/ machen soll zsh.

Wie kann dies innerhalb eines Shell-Skripts als solches erfolgen?

ylluminate
quelle
@Sundeep, dass letztere Option besser für die Parametererweiterung ist. Wie funktioniert das genau? Im Moment bekommst du in deinem Beispiel das YYYYund MM, aber dann schnappst du dir einfach den Rest mit ${f:8}, wenn ich lieber einfach greifen DDund wegwerfen möchte .*(alles danach DD).
ylluminate
Könnten Sie bitte eine gewünschte Ausgabe posten? Oder ist Ihr Ziel, Dateien umzubenennen?
John Goofy
@ JohnGoofy Bitte beachte meine Bearbeitung von vor ~ 30 Minuten und die Antwort, die Sundeep gegeben hat.
ylluminate

Antworten:

9

Mit Parameter Expansion

$ touch 2014-11-19.8.ext 2014-11-26.1.ext
$ for f in *.ext; do d="${f:0:4}${f:5:2}${f:8:2}"; echo "$d"; done
20141119
20141126
  • ${f:0:4}bedeutet 4 Zeichen ab Index 0und fist Variablenname
  • durch echo "$d"deinen Code ersetzen
Sundeep
quelle
Interessant, also befindet sich der "Cursor" im Index und ist nicht inklusive, sondern exklusiv. 5:2beginnt in diesem Fall also mit dem 1. Strich, schließt ihn aber nicht ein. 8:2beginnt am 2. Strich und enthält ihn nicht. Sehr interessant und toll zu wissen.
ylluminate
0startet Index ... also ist der erste -Index 4...
Sundeep
Ich stelle mir Indizes immer so vor, dass sie zwischen Zeichen zeigen und nicht auf sie. Der Oddball-Fall fordert dann ein einzelnes Zeichen an. In diesem Fall steht der Index für "zwischen diesem Index und dem nachfolgenden Index", dh das Zeichenrecht dieses Index.
BallpointBen
5

Um jede Datei im aktuellen Verzeichnis zu durchlaufen und ihre Dateinamen mit dem gewünschten Muster zu vergleichen, legen Sie eine Variable fest, die die Datumsangaben enthält

for f in *
do 
  [[ $f =~ ^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])(.*) ]] && 
  yourvar="${BASH_REMATCH[1]}${BASH_REMATCH[2]}${BASH_REMATCH[3]}"
done

Dies nutzt die [[Fähigkeit von bash , reguläre Ausdrücke zu verwenden, um die Datumsangaben in das Array BASH_REMATCH zu platzieren.

Jeff Schaller
quelle
3

Sie können dies interaktiv tun, indem Sie Folgendes verwenden GNU sed:

$ sed 's/^\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}.*\)/\1\2\3/g' stuff.txt

Für mehrere Dateien (wenn sie sich im selben Verzeichnis befinden und keine anderen berücksichtigten Dateien im Verzeichnis):

for file in *
do
    if [ -f "$file" ]
    then
          sed 's/^\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\).*/\1\2\3/g' "$file"
    fi
done
FloHe
quelle
Okay, nett, aber ich persönlich mag die Prägnanz der Kommentare, die @Sundeep oben hinterlässt, wo Sie eine besser lesbare Kontrolle über die Felder zu haben scheinen. Mein Ziel hier ist es, diese Elemente zu extrahieren und sie dann in einem anderen Befehl zu verwenden (insbesondere stelle ich Zeiten über ein touch). Ich
bin
Sie können die Ausgabe zB an einen anderen Befehl weiterleiten.
FloHe
2

Hier ist eine zshMöglichkeit, dies ohne Schleifen zu tun:

autoload -U zmv
zmv -n '([0-9](#c4))-([0-9](#c2))-([0-9](#c2))(*)' '$1$2$3$4'
  • [0-9](#c4) bedeutet, dass jede Ziffer viermal wiederholt wird
  • $1- $2Siehe zuvor verwendete Klammern
  • -n verhindert die Ausführung (nur Drucke), entfernen Sie dieses Flag, wenn Sie mit dem Ergebnis zufrieden sind

Da zshsich um das Globbing kümmert, sollten alle Eckfälle (Leerzeichen, Sonderzeichen usw.) automatisch berücksichtigt werden.

jimmij
quelle
2

Wenn Sie auf GNU Coreutils sind, haben Sie Folgendes:

$ date --date=2014-11-13 +"%Y%m%d"
20141113

Jedoch:

$ date --date=2014-11-130ABCJUNK +"%Y%m%d"
date: invalid date 2014-11-130ABCJUNK

Die Aufgabe ist also viel einfacher: Extrahieren Sie die ersten zehn Zeichen jedes YYYY-MM-DDetcDateinamens, um das Datum selbst zu erhalten, und übergeben Sie es dann datezur Neuformatierung an.

Wenn wir uns jedoch auf GNU Coreutils befinden, können wir den dateBefehl überspringen, da er touchgenau die gleiche --date=STRINGOption hat.

for file in * ; do
  date=${file%${file##??????????}} # chop all but first ten
  touch --date=$date -- "$file"
done

Aber warum hacken diese zehn Zeichen auf tragbare Weise mit POSIX, wenn wir uns darauf verlassen touch, von GNU Coreutils zu stammen?

for file in * ; do
  date=${file:0:10}
  touch --date=$date -- "$file"
done
Kaz
quelle
Mir wurde von jemandem gesagt, touchder YYYYMMDDnur Format benötigte , als der -tParameter ausgegeben wurde ...
ylluminate
@ylluminate: -terfordert [[cc] yy] mmddhhmm [.ss] - was nicht dasselbe ist, wie Sie geschrieben haben, obwohl keine andere Interpunktion als möglicherweise ein Punkt weggelassen wird -, sondern in der GNU-Version (wie klar angegeben) --date(oder -d) ist anders.
Dave_thompson_085
1

Versuchen Sie die Mustersubstitution:

${parameter/pattern/string}

Parameter ist der Basisname der Datei. Muster ist ein Strich. In diesem / - Muster global ersetzen. Zeichenfolge ist leer, da Sie die Bindestriche löschen möchten.

mv "${f}" "${f//-/}"

Vorsichtsmaßnahme: Ich habe das mit dem Fall von Leerzeichen in der Erweiterung nicht zum Laufen gebracht.

Martin Lange
quelle
In diesem Fall möchte ich also nicht nur die Striche löschen, sondern alles nach dem DDim Muster. Das Ziel ist es, JJJJMMTT zu extrahieren, um es in einem touchBefehl zu verwenden, nachdem das richtige Muster erhalten wurde, da ich den Dateinamen selbst intakt lassen und einfach die Datierung für den Zeitstempel des Dateisystems herausziehen werde. Könnte die obigen Kommentare von @Sundeep sehen, soweit sie nahe beieinander liegen, aber ich folge bis jetzt nicht der tatsächlichen Logik in der Parametererweiterungskette.
ylluminate
@ylluminate Sie können eine zweistufige Ersetzung wie folgt durchführen for f in *.ext; do d="${f%%.*}"; echo "${d//-}"; done(entfernen Sie zuerst die längste nachfolgende Zeichenfolge und dann die Bindestriche).
Steeldriver