ubuntuusers.de

Find -- copy

Status: Gelöst | Ubuntu-Version: Kubuntu 20.04 (Focal Fossa)
Antworten |

michahe

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Hallo,

ich möcte per find Dateie selektieren und in anderes Verzeichnis mit erweitertem Dateinamen kopieren; mein Konzept:

1
$ find "/Pfad/*.odt" -ctime -14 -exec copy {} "/neuerPfad/" \;

Meine Fragen:

  • Wie definiere ich den Pfad korrekt? Syntax des Befehls?

  • Wie bekomme ich einen Zeitstempel in die Dateinamen, d.h. aus "Text 1.odt" soll "Text 1 SICHER YYYYMMDD.odt" mit aktuellem Datum werden.

Danke!

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17625

Wohnort: Berlin

michahe schrieb:

Hallo,

ich möcte per find Dateie selektieren und in anderes Verzeichnis mit erweitertem Dateinamen kopieren; mein Konzept:

1
$ find "/Pfad/*.odt" -ctime -14 -exec copy {} "/neuerPfad/" \;

Meine Fragen:

  • Wie definiere ich den Pfad korrekt? Syntax des Befehls?

  • Wie bekomme ich einen Zeitstempel in die Dateinamen, d.h. aus "Text 1.odt" soll "Text 1 SICHER YYYYMMDD.odt" mit aktuellem Datum werden.

Der Pfad ist die Liste der Verzeichnisse, in denen eine Datei ist, nicht die Datei selbst.

1
find "/Pfad" -name "*.odt" -ctime -14 -exec copy {} "/neuerPfad/" \;

womöglich ist

1
find "/Pfad" -name "*.odt" -ctime -14 -execdir copy {} "/neuerPfad/" \;

eher das, was Du möchtest - die Dateien flach ins Ziel kopieren, nicht die Unterverzeichnisstruktur mitkopieren.

michahe

(Themenstarter)

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Danke: user_unknown schrieb:

find "/Pfad" -name "*.odt" -ctime -14 -exec copy {} "/neuerPfad/" \;

womöglich ist

find "/Pfad" -name "*.odt" -ctime -14 -execdir copy {} "/neuerPfad/" \;

eher das, was Du möchtest - die Dateien flach ins Ziel kopieren, nicht die Unterverzeichnisstruktur mitkopieren.

in beiden Fällen erhalte ich ("neuerPfad" existiert!):

find: ‘copy’: Datei oder Verzeichnis nicht gefunden

Der find-Teil liefert:

$ find "/aktuellerPfad/" -name "*.odt" -ctime -14
/aktuellerPfad/Test.odt

(Nur) die gefundene Datei soll in das (existierende) Verzeichnis "neuerPfad" kopiert werden mit "SICHER" und Zeitstempel im Namen:

/neuerPfad/Test SICHER 20230226.odt

Und: Könnte ich die Berechtigungen der Kopie auf nur Lesen setzen?

dirkolus

Anmeldungsdatum:
17. Mai 2011

Beiträge: 2178

Wohnort: dahoam

michahe schrieb:

user_unknown schrieb:

find "/Pfad" -name "*.odt" -ctime -14 -exec copy {} "/neuerPfad/" \;

in beiden Fällen erhalte ich ("neuerPfad" existiert!):

find: ‘copy’: Datei oder Verzeichnis nicht gefunden

Nach -exec kommt einfach ein Shell-Befehl, und das ist normalerweise cp und nicht 'copy'.

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

michahe schrieb:

(Nur) die gefundene Datei soll in das (existierende) Verzeichnis "neuerPfad" kopiert werden mit "SICHER" und Zeitstempel im Namen:

/neuerPfad/Test SICHER 20230226.odt

Und: Könnte ich die Berechtigungen der Kopie auf nur Lesen setzen?

Ja, klar. Dafür braucht man dann aber einen zweiten Schritt. Dann müsstest Du das so oder ähnlich machen:

1
2
3
4
5
6
7
8
9
export TS=$(date +%Y%m%d)

find /quelle -type f -name \*.odt -ctime -14 -exec sh -ec '
for f; do
  tgt="/ziel/$(basename "$f" .odt) SICHER $TS.odt"
  cp -a --reflink=auto "$f" "$tgt"
  chmod -w "$tgt"
done
' -- {} +

Den "--reflink=auto" habe ich eingefügt, damit auf btrfs weniger Platz und IO verbraucht wird.

michahe

(Themenstarter)

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Danke rklm für den Denkanstoß.

Und: Könnte ich die Berechtigungen der Kopie auf nur Lesen setzen?

Ja, klar. Dafür braucht man dann aber einen zweiten Schritt. Dann müsstest Du das so oder ähnlich machen:

Ich komme mit basename und Leerzeichen im Dateinamen nicht klar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
clear
files=$(find "/Pfad/" -name "*.odt" -ctime -42)
for f in "$files"; do
   echo "$f"
done
echo "Teil2"
for f in "$files"; do
   target=$(basename "$f")
   echo "$target"
done

liefert:

/Pfad/BBBB 22.odt
/Pfad/AAAA.odt
Teil2
AAAA.odt

Wo ist mein Fehler?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

michahe schrieb:

Danke rklm für den Denkanstoß.

Bitte. Du hast ihn allerdings nicht aufgenommen, soweit ich das sehe.

Wo ist mein Fehler?

Schon in Zeile 2. Erstens kannst Du so die Dateinamen nicht in einem Array speichern und zweitens geht das wegen der Leerzeichen schief, selbst, wenn Du das mit dem Speichern im Array richtig machst. Zeile 3 und 7 müssen auch anders aussehen, wenn Du die Dateinamen in einem Array hast. Aber siehe oben.

Eine andere Methode, das ordentlich in einem Array zu handhaben, die sogar mit der sh funktioniert:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/sh

if [ $# -eq 0 ]; then
  export TS=$(date +%Y%m%d)
  find /quelle -type f -name \*.odt -ctime -14 -exec "$0" -- {} +
else
  for f; do
    tgt="/ziel/$(basename "$f" .odt) SICHER $TS.odt"
    cp -a --reflink=auto "$f" "$tgt"
    chmod -w "$tgt"
  done
fi

michahe

(Themenstarter)

Anmeldungsdatum:
12. Dezember 2013

Beiträge: 857

Danke rklm, Deine Lösung

Eine andere Methode, das ordentlich in einem Array zu handhaben, die sogar mit der sh funktioniert:

verstehe ich nicht:

  • Warum if .. else?

  • Was heißt [ $# -eq 0 ] (hier), insbesondere da noch gar nichts definiert ist?

  • Was heißt -exec "$0" – {} +?

Ich möchte nicht einfach Code kopieren, sondern eine Lösung, die ich auch in einem Jahr noch verstehe; mein bash-Wissen ist weitaus geringer als Deines.

Deshalb habe ich jetzt dieses Konstrukt, das hier korrekt funktioniert:

clear
DateTime=$(date +%Y%m%d%H%M%S)
find "/quelle/" -name "*.odt" -ctime -42 | while read fname; do
#   echo "$fname";
    target="/ziel/$(basename "$fname" .odt) SICHER $DateTime.odb"
    echo $target
done;

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13219

michahe schrieb:

Danke rklm, Deine Lösung

Eine andere Methode, das ordentlich in einem Array zu handhaben, die sogar mit der sh funktioniert:

verstehe ich nicht:

  • Warum if .. else?

  • Was heißt [ $# -eq 0 ] (hier), insbesondere da noch gar nichts definiert ist?

Beim ersten Aufruf werden keine Argumente an das Skript übergeben, dann wird der find gestartet und der find startet dann das Skript mit mindestens einem Argument, wenn passende Dateien gefunden werden. Dann arbeitet das Skript auf den übergebenen Dateinamen.

  • Was heißt -exec "$0" – {} +?

$0 ist der Pfad zum aktuellen Skript (das findet sich auch in der bash/sh Doku unter "Special Parameters"). "{} +" wird ersetzt durch eine Liste von Dateinamen (siehe Doku von find). "–" ist lediglich ein Platzhalter, weil sonst der erste übergebene Name einer gefundenen Datei in $0 gespeichert und dann nicht von der for-Schleife erfasst wird.

Ich möchte nicht einfach Code kopieren, sondern eine Lösung, die ich auch in einem Jahr noch verstehe; mein bash-Wissen ist weitaus geringer als Deines.

Das ist lobenswert.

Deshalb habe ich jetzt dieses Konstrukt, das hier korrekt funktioniert:

1
2
3
4
5
6
7
clear
DateTime=$(date +%Y%m%d%H%M%S)
find "/quelle/" -name "*.odt" -ctime -42 | while read fname; do
#   echo "$fname";
    target="/ziel/$(basename "$fname" .odt) SICHER $DateTime.odb"
    echo $target
done;

Da gibt es einen Sonderfall: Wenn ein Newline im Dateinamen vorkommt, funktioniert es nicht. Und, da Du nicht read -p verwendest, geht es auch bei Backslashes im Dateinamen schief. Aber das sind nur Randfälle.

Antworten |