Wie erfasse ich einen Ausdruck mit dem Bash-Terminal?

Wie erfasse ich einen Ausdruck mit dem Bash-Terminal?

Ich habe Dateien mit derselben Nomenklatur log_<date>.txtin einem verschachtelten Verzeichnis verstreut root. Wo <date>steht das Erstellungsdatum der Dateien im Format JJJJMMTTHHMM? Aus Gründen der Codekompatibilität möchte ich alle Dateien umbenennen in<date>_log.txt

Ich weiß, dass ich den Ausdruck erfassen müsste <date>, aber wie mache ich das in Bash? Und wie mache ich das in einem verschachtelten Verzeichnis?

Antwort1

Angenommen, Sie haben den Namen einer dieser Dateien in der Shell-Variable name. Das Entfernen des log_Bits aus dem Wert der Variable erfolgt dann mit ${name#log_}, und das Entfernen des .txtBits erfolgt mit ${name%.txt}. Was übrig bleibt, ist das Datum, und in welchem ​​Format es vorliegt, ist im Wesentlichen nicht interessant. Es wäre einfach, den entfernten Wert zu nehmen und _log_.txtam Ende hinzuzufügen, um den neuen Namen zu erstellen.

Um dies für die Dateien in einem einzigen Verzeichnis zu tun, verwenden Sie eine Schleife. Beachten Sie, dass wir jetzt auch den Verzeichnisnamen für den Anfang des Variablenwerts entfernen müssen.

shopt -s nullglob

for name in root/log_*.txt; do
    newname=root/${name#root/log_}    # remove prefix, add root/ back
    newname=${newname%.txt}_log.txt   # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

Den eigentlichen Befehl habe ich aus Sicherheitsgründen auskommentiert mv. Du solltest ihn einmal probeweise ausführen, um zu sehen, was zuerst passiert.

Mit der nullglobShell-Option wird die bashShellentfernennicht übereinstimmende Muster, anstatt sie unerweitert zu lassen. Das bedeutet, dass die Schleife überhaupt nicht ausgeführt würde, wenn sich log_*.txtim angegebenen Verzeichnis keine Dateien befänden.

Dies kann auf jede solche Datei im aktuellen Verzeichnis (einschließlich des aktuellen Verzeichnisses) rekursiv angewendet werden durchmehr oder wenigerÄndern Sie einfach das Muster, über das wir eine Schleife durchführen:

shopt -s globstar nullglob

for name in ./**/log_*.txt; do
    dirpath=${name%/*}                      # get path of directory
    newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
    newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

    printf 'Would move "%s" to "%s"\n' "$name" "$newname"
    # mv -i "$name" "$newname"
done

Zu den weiteren Änderungen gehört die Aktivierung der globstarShell-Option, mit der wir **in Unterverzeichnissen nacheinander suchen können. Außerdem wähle ich den Verzeichnispfad aus, um ihn dem neuen Namen voranstellen zu können.

findSie können zur Generierung der Eingabe für die Schleife auch Folgendes verwenden :

find . -type f -name 'log_*.txt' -exec sh -c '
    for name do
        dirpath=${name%/*}                      # get path of directory
        newname=$dirpath/${name#$dirpath/log_}  # remove prefix, add directory path back
        newname=${newname%.txt}_log.txt         # remove suffix, add _log.txt back

        printf "Would move \"%s\" to \"%s\"\n" "$name" "$newname"
        # mv -i "$name" "$newname"
    done' sh {} +

Dadurch werden alle regulären Dateien gefunden, deren Namen log_*.txtim aktuellen Verzeichnis oder darunter übereinstimmen, und diese werden dann stapelweise an ein Inline- sh -cSkript übergeben, das im Wesentlichen dieselbe Arbeit ausführen würde wie die Schleife in der zweiten Variante oben.

Antwort2

Am besten verwenden Sie ein Tool zum Umbenennen von Batchdateien wie oder zsheine der Perl-basierten Varianten von :zmvmmvrename

zsh << 'EOF'
autoload zmv
zmv 'log_(*).txt' '${1}_log.txt'
EOF

Oder

prename 's/log_(.*)\.txt/${1}_log.txt/' log*.txt

verwandte Informationen