Shell: verschieben der Dateien

Post Reply
Message
Author
micbur
Posts: 86
Joined: 05. Jun 2004 15:55

Shell: verschieben der Dateien

#1 Post by micbur »

Hallo,

ich habe da ein kleines Problem. Ich denke, wenn jemand mit der Bash umgehen kann, das ist es für ihn kein Problem, für mich ist es leider eines.

Ich habe in einem Verzeichnis durch logrotate ganz viele Unterverzeichnisse. Verzeichnistiefe ist maximal 1. In jedem Unterverzeichnis befinden sich verschiedene logs.
Beispiel:
$dir/subdir1/file.gz
$dir/subdir2/file.gz
$dir/subdir3/file.gz

Nun möchte ich die aber zur Weiterverarbeitung kopiert/verschoben haben, sodass der Verzeichnisname sich im Dateinamen nachher wiederfindet.
Beispiel:
$dir/file_subdir1.gz
$dir/file_subdir2.gz
$dir/file_subdir3.gz

So richtig komme ich nicht weiter.
Ich kann mir zwar alle Dateien ab einer bestimmten Verzeichnistiefe anzeigen lassen, das sogar auf verschiedenen Wegen, aber ich komme leider nicht soweit, dass ich die Dateien kopieren kann.

Entweder: find $PWD -type f -name *gz
oder:
find $PWD -depth -type d -print | while read DIR ;
do
echo $DIR;
done

Ich weiß nicht recht, wie ich mir den Verzeichnisnamen 'merken' kann, damit ich den dann weiter verwenden kann.
Es ist zwar blamabel, aber ich brauche Hilfe.

Ciao, micbur

User avatar
jochen
prolinux-forum-admin
Posts: 699
Joined: 14. Jan 2000 15:37
Location: Jülich
Contact:

#2 Post by jochen »

Hi, micbur,

hier muss man entweder mit sed oder in der bash mit geschickter Variablensubstitution arbeiten...

Code: Select all

#!/bin/bash
EXT=gz

find . -name "*.$EXT" -type f | \
while read FULLNAME ; do
        FILENAME=${FULLNAME##*/}
        FILENAME=${FILENAME%.$EXT}
        DIRNAME=${FULLNAME%/*}
        DIRNAME=${DIRNAME##*/}
        echo cp $FULLNAME ${FILENAME}_$DIRNAME.$EXT
done
find liefert die zu beharkenden Dateien. Die while-Schleife läuft pro Dateinamen einmal durch und setzt den aktuellen Namen in die Variable FULLNAME. Aus dieser wird der Dateiname (alles hinter dem letzten /) extrahiert und die Dateiendung abgeschnitten. Damit wäre der Dateiname extrahiert und steht in FILENAME.
Der Verzeichnisname wird ähnlich extrahiert: Erst alles ab dem letzten Slash entfernen (also den Dateinamen und dann alles bis zum dann noch letzten Shash entfernen - übrig bleibt der Name des Verzeichnisses, in dem die Datei direkt steht. Daraus dann nur noch einen neuen Namen basteln und ein cp davor hängen - fertig!

Nur eins noch: Ich habe das Skript mittels "echo" vo dem cp-Kommando entschärft - nach ausgiebigem Testen und Anpassen bitte entfernen!

Wenn Du genauer wissen willst, was bei ${VAR##*/} und so passiert, dann schau in der bash-Manualpage mal unter dem Abschnitt "Parameter Expansion" nach.

Viel Spass damit!

Jochen
Die grösste Lüge der EDV? "Mal eben..."

micbur
Posts: 86
Joined: 05. Jun 2004 15:55

#3 Post by micbur »

Vielen Dank.

Das geht ja klasse.

Ich habe die ganze Zeit noch versucht mit Regex und cp mein Ziel zu erreichen.
Dabei wollte ich gerade die Zeile etwa so aussehen lassen:
cp -vr */*.gz archiv/$2_$1.gz

Also ich hatte so eine Ahnung, dass man im Suchmuster Regex benutzen kann, die man dann im Zielmuster verwenden kann. Das hat aber leider nicht so geklappt.

Vielen Dank für dein Script. Habe es schon angepasst, damit nicht zu viel kopiert wird. Wer intresse hat, ich werde noch ein bisschen rumtesten und ein paar Anpassungen vornehmen, und es hier dann reinstellen.

Ciao, micbur

micbur
Posts: 86
Joined: 05. Jun 2004 15:55

#4 Post by micbur »

Wie versprochen, hier der gesamte Code

Code: Select all

#!/bin/sh
#
# Dieses Script kopiert/verschiebt alle Dateien aus einem Verzeichnis mit Unterverzeichnissen in
# ein anders Verzeichnis und benennt dabei die Dateien um, in einer Namenskomination aus
# Verzeichnisnamen und altem Dateinamen.
#
#    Beispiel:
# von:
# $dir/quelle/subdir1/file
# $dir/quelle/subdir2/file
#
# nach:
# $dir/ziel/file_subdir1
# $dir/ziel/file_subdir2
#

# wir suchen nur gezippte Logfiles, also brauchen wir auch nur nach *gz zu suchen
EXT=gz

# Zielverzeichnis
TARGET=/mount-point

umount /mount-point
mount -t smbfs -o ip=***,username=***/***,password=*** //***/*** /mount-point

# wir brauchen nur die access-logs, alles andere ist nicht analysierbar
find $PWD -name "*access*.$EXT" -type f | \
while read FULLNAME ; do

# der Dateiname ist alles nach dem letztem Slash
        FILENAME=${FULLNAME##*/}

# der Dateiname ohne Dateiendung
        FILENAME=${FILENAME%.$EXT}

# der Verzeichnisname ist immer vor dem letztem Slash
        DIRNAME=${FULLNAME%/*}

# im Ergebnis habe ich nun den Verzeichnisnamen, der sich zwischen den beiden letzten Slashes befand
        DIRNAME=${DIRNAME##*/}

# nun endlich das Kopieren/Verschieben
# rsync lohnt nicht, weil mit dem Kopieren die Dateien auch umbenannt werden
# Parameter: --reply=no, bei jedem Dialog wird mit 'no' geantwortet, zum Beispiel beim Überschreiben
# Parameter: -v, viele Ausgaben zu jedem Kopiervorgang
        cp --reply=no -v $FULLNAME $TARGET/${FILENAME}_$DIRNAME.$EXT
done

User avatar
jochen
prolinux-forum-admin
Posts: 699
Joined: 14. Jan 2000 15:37
Location: Jülich
Contact:

#5 Post by jochen »

Hi micbur!

Ich zitier hier mal die Änderung, die Du benötigst, aus Deiner PN:
Es handelt sich ja um Apache-Access-Logs, die von logrotate gezippt und verschoben werden. Nun haben wir aber noch eine zweite Applikation, die in einem Unterverzeichnis arbeitet.
der normale Apache mit logrotate arbeiten so:
/var/log/apache/<date>/*log.gz

Die andere Applikation arbeitet in einem anderen Verzeichnis:
/var/log/apache/six/<date>/*log.gz

Nun wurden durch das Skript alle Logs in ein Verzeichnis gepackt und können nicht mehr unterschieden werden. Also wäre es besser, wenn ich eine Lösung finde, in der der Verzeichnisname ab einem bestimmten Punkt genutzt wird, so wie
$SOURCE=/var/log/apache
$SOURCE/<dir>/<subdir>/file -> $TARGET/dir_subdir_file
(Ja, ich weiss auch, dass man PNs oder Auszüge daraus nicht ungefragt öffentlich zitiert. Aber das hier ist ja nun hinreichend zweckgebunden & unpersönlich. OK?)

Die Lösung zum Problem ist sogar einfacher als die ursprüngliche, da man dort den Verzeichnisnamen und den Dateinamen getrennt behandeln musste. Schließlich wurde der Name da "umgekehrt" wieder zusammengesetzt.

DIe grundlegende Lösung sieht aus wie folgt:

Code: Select all

#!/bin/bash
EXT=gz

STARTDIR=/var/apache/logs/
TARGETDIR=/wohin/meine/logs/kommen/

find $STARTDIR -name "*.$EXT" -type f | \
while read FULLNAME ; do
        FILENAME=$&#123;FULLNAME#$STARTDIR&#125;
        FILENAME=$&#123;FILENAME//\//-&#125;
        echo cp $FULLNAME $&#123;TARGETDIR&#125;$&#123;FILENAME&#125;
done
Auch dieses Skript ist wieder zu Testzwecken entschärft (echo vor dem cp-Kommando -> Ausgabe, was passieren würde, aber keine echte Aktion).

EXT, STARTDIR und TARGETDIR dürften selbsterklärend sein.

Die eigentlichen Änderungen stecken im Schleifenkörper. Die Zeile

Code: Select all

FILENAME=$&#123;FULLNAME#$STARTDIR&#125;
löscht das Startverzeichnis aus dem Dateinamen und weist das Ergebnis der Variablen FILENAME zu. In der haben wir jetzt alle Verzeichnisse und Unterverzeichnisse und den Dateinamen stehen. Eigentlich schon genau das, was wir wollen, aber die Slashes stören und sollten lieber Bindestriche sein. Das erreichen wir mittels

Code: Select all

FILENAME=$&#123;FILENAME//\//-&#125;
In der Variablen FILENAME "${FILENAME" wird alles ersetzt "//", was ein Slash "\/" ist, und zwar durch "/" ein Minuszeichen "-". Fertig "}"!

Beispiel: Aus "/var/apache/logs/six/irgendwas.gz" wird durch die erste Zeile "six/irgendwas.gz" und durch die zweite dann "six-irgendwas.gz".

Damit ist der Zieldateiname komplett und man braucht nur noch das Zielverzeichnis davorzuflanschen.

Alles klar? Viel Spass beim Anpassen der Lösung! :-)

Jochen
Die grösste Lüge der EDV? "Mal eben..."

der_micbur

#6 Post by der_micbur »

Vielen Dank Jochen,
ich habe das auch gleich getestet. Funktioniert prima.

Danke, micbur

andib
Posts: 277
Joined: 20. Jun 2000 14:18
Location: Dortmund

#7 Post by andib »

Wie wäre es mit

Code: Select all

mmv "*/*.*" "#1/#2_#1.#3"
Erläuterung: auf der Linken Seite werden alle Dateien der Form */*.* gematcht.
Auf der rechten Seite wird das n-te Wildcard ("*") durch #n ersetzt.
dir/file.gz
wird also nach
dir/file_dir.gz
verschoben etc.

Für solche Sachen ist mmv immer hübsch einfach :-)

CU
Andi

der_micbur

#8 Post by der_micbur »

Hallo andib,

ja, das waere auch ganz toll, aber ich habe hier Apache-Logs, die mittels logrotate rotiert werden. Bei der Namenskonvention, fuer die wir uns entschieden haben, koennen an einem Tag die Logs auch schonmal mehrfach rotoeren, was dazu fuehrt, dass wur dann Logs haben, die solche Namen haben wie ...

apache_access.2.gz oder
apache_access.gz oder unkomprimiert
apache_access.log

Da wir also mehrere Punkte im Namen haben und es nicht vorhersehbar ist, ob und wann mehrere Logs pro Tag auftreten, kann man leider deine Loesung nicht nehmen.

Aber trotzdem vielen Dank. Ich dachte eigentlich, dass die Tools in fileutils alls regulaere Ausdruecke bzw. Platzhalter als Eingabe verstehen.

Ciao, micbur

Post Reply