Behalten Sie die Verzeichnisstruktur bei, wenn Sie Dateien mit find verschieben

22

Ich habe das folgende Skript erstellt, mit dem alte Dateien aus dem Quellverzeichnis in das Zielverzeichnis verschoben werden. Es funktioniert perfekt.

#!/bin/bash

echo "Enter Your Source Directory"
read soure

echo "Enter Your Destination Directory"
read destination 

echo "Enter Days"
read days



 find "$soure" -type f -mtime "-$days" -exec mv {} "$destination" \;

  echo "Files which were $days Days old moved from $soure to $destination"

Dieses Skript verschiebt großartige Dateien. Es verschiebt auch Dateien des Quell-Unterverzeichnisses, erstellt jedoch kein Unterverzeichnis im Zielverzeichnis. Ich möchte dieses zusätzliche Feature darin implementieren.

mit beispiel

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

Wenn ich dieses Skript ausführe, verschiebt es auch hexadezimale Dateien in das Maxi-Verzeichnis. Ich benötige jedoch dasselbe Hexadezimale, das in das Maxi-Verzeichnis erstellt werden soll, und verschiebt die Dateien dorthin in dasselbe Hexadezimale.

Ketan Patel
quelle

Antworten:

13

Anstatt ausgeführt zu werden mv /home/ketan/hex/foo /home/maxi, müssen Sie das Zielverzeichnis basierend auf dem von erzeugten Pfad variieren find. Dies ist einfacher, wenn Sie zuerst in das Quellverzeichnis wechseln und es ausführen find .. Jetzt können Sie das Zielverzeichnis lediglich jedem von erstellten Element voranstellen find. Sie müssen im find … -execBefehl eine Shell ausführen, um die Verkettung durchzuführen und gegebenenfalls das Zielverzeichnis zu erstellen.

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  mkdir -p "$0/${1%/*}"
  mv "$1" "$0/$1"
' "$destination" {} \;

Beachten Sie, dass $destinationSie das Anführungszeichen nicht einfach im Shell-Skript ersetzen können , um Anführungszeichen zu vermeiden, wenn es Sonderzeichen enthält. Sie können es in die Umgebung exportieren, damit es die innere Hülle erreicht, oder Sie können es als Argument übergeben (das habe ich getan). Sie können ein wenig Ausführungszeit sparen, indem Sie shAufrufe gruppieren :

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  for x do
    mkdir -p "$0/${x%/*}"
    mv "$x" "$0/$x"
  done
' "$destination" {} +

Alternativ können Sie in zsh die Qualifikationsmerkmale zmvfunction und .und m glob verwenden, um nur mit regulären Dateien im richtigen Datumsbereich übereinzustimmen. Sie müssen eine alternative mvFunktion übergeben, die bei Bedarf zuerst das Zielverzeichnis erstellt.

autoload -U zmv
mkdir_mv () {
  mkdir -p -- $3:h
  mv -- $2 $3
}
zmv -Qw -p mkdir_mv $source/'**/*(.m-'$days')' '$destination/$1$2'
Gilles 'SO - hör auf böse zu sein'
quelle
for x do, da fehlt dir was ;:). Ich habe auch keine Ahnung, was Sie damit erreichen wollten, $0aber ich bin ziemlich überzeugt, dass es so sein würde sh:).
Michał Górny
@ MichałGórny for x; doist technisch nicht POSIX-konform ( Grammatik prüfen ), moderne Shells erlauben jedoch beides for x dound for x; do; einige alte Borowski-Muscheln gruben nicht for x; do. Über moderne Muscheln, mit sh -c '…' arg0 arg1 arg2 arg3, arg0wird $0, arg1wird $1usw. Wenn Sie sein wollen $0, müssen shSie schreiben sh -c '…' sh arg1 arg2 arg3. Auch hier haben sich einige Bourne-Shells anders verhalten, aber POSIX gibt dies an.
Gilles 'SO- hör auf böse zu sein'
pushdDas scheint eine bessere Wahl zu sein cd, da es weniger aufdringlich für die aktuelle Umgebung ist.
jpmc26
16

Ich weiß, findwurde angegeben, aber das klingt nach einem Job für rsync.

Ich benutze am häufigsten die folgenden:

rsync -axuv --delete-after --progress Source/ Target/

Hier ist ein gutes Beispiel, wenn Sie nur Dateien eines bestimmten Dateityps verschieben möchten ( Beispiel ):

rsync -rv --include '*/' --include '*.js' --exclude '*' --prune-empty-dirs Source/ Target/
Ryanjdillon
quelle
viel sauberer als die anderen Lösungen :)
Guillaume
2
Ich fand das --remove-source-fileshilfreich, was effektiv dazu führt, dass Dateien verschoben statt kopiert werden. Dies ist eine großartige Anwendung von rsync.
Tom
3

Sie könnten es mit zwei Instanzen von find (1) tun

Es gibt immer cpio (1)

(cd "$soure" && find  | cpio -pdVmu "$destination")

Überprüfen Sie die Argumente für cpio. Die, die ich gegeben habe

Mark Lamourine
quelle
1
Dies bricht bei Dateinamen mit Leerzeichen ab.
Chris Down
1
Dadurch werden Dateien kopiert, anstatt sie zu verschieben.
Gilles 'SO- hör auf böse zu sein'
Es mag keine perfekte Antwort sein, aber es hat mir geholfen, Dateien unter Beibehaltung der Pfade mehr oder weniger einfach zu verschieben (ich habe genug Platz, um die Dateien zu kopieren und dann zu löschen). Upvoted
AhHatem
3

Es ist nicht so effizient, aber der Code ist meiner Meinung nach leichter zu lesen und zu verstehen, wenn Sie nur die Dateien kopieren und anschließend löschen.

find /original/file/path/* -mtime +7 -exec cp {} /new/file/path/ \;
find /original/file/path/* -mtime +7 -exec rm -rf {} \;

Hinweis: Von @MV entdeckter Fehler bei automatisierten Vorgängen:

Die Verwendung von zwei getrennten Operationen ist riskant. Wenn einige Dateien älter als 7 Tage sind, während der Kopiervorgang ausgeführt wird, werden sie nicht kopiert, sondern durch den Löschvorgang gelöscht. Wenn etwas einmal manuell ausgeführt wird, ist dies möglicherweise kein Problem, bei automatisierten Skripten kann dies jedoch zu Datenverlust führen

Elliott Post
quelle
2
Die Verwendung von zwei getrennten Operationen ist riskant. Wenn einige Dateien älter als 7 Tage sind, während der Kopiervorgang ausgeführt wird, werden sie nicht kopiert, sondern durch den Löschvorgang gelöscht. Wenn etwas einmal manuell ausgeführt wird, ist dies möglicherweise kein Problem, bei automatisierten Skripten kann dies jedoch zu Datenverlust führen.
MV.
2
Die einfache Lösung für diesen Fehler besteht darin, find einmal auszuführen, die Liste als Textdatei zu speichern und dann xargs zweimal zu verwenden, um das Kopieren und dann das Löschen durchzuführen.
David M. Perlman
1

Sie können dies tun, indem Sie den absoluten Pfad der von zurückgegebenen Datei findan Ihren Zielpfad anhängen :

find "$soure" -type f -mtime "-$days" -print0 | xargs -0 -I {} sh -c '
    file="{}"
    destination="'"$destination"'"
    mkdir -p "$destination/${file%/*}"
    mv "$file" "$destination/$file"'
Chris Down
quelle
Chris zieht jetzt ganz nach Hause ins Maxi
Ketan Patel
@KKPatel Nein, das tut es nicht. Es wird lediglich die Verzeichnisstruktur beibehalten.
Chris Down
Dies bricht ab, wenn $destinationSonderzeichen enthalten sind, da es in der inneren Hülle eine Erweiterung erfährt. Vielleicht meintest du destination='\'"$destination"\''? Das bricht noch an '. Hierdurch werden auch Dateien erstellt, z. B. /home/maxi/home/ketan/hex/fooanstelle von /home/maxi/hex/foo.
Gilles 'SO- hör auf böse zu sein'
0

Besser (am schnellsten und ohne Speicherplatz zu verbrauchen, indem Kopieren statt Verschieben ausgeführt wird), sind die Dateinamen auch dann nicht betroffen, wenn sie Sonderzeichen in ihren Namen enthalten:

export destination
find "$soure" -type f "-$days" -print0 | xargs -0 -n 10 bash -c '
for file in "$@"; do
  echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
  mkdir -p "$destination"/`dirname "$file"`
  \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
done'

Oder schneller: Verschieben einer Reihe von Dateien für mehrere CPUs gleichzeitig mit dem Befehl "parallel":

echo "Move oldest $days files from $soure to $destination in parallel (each 10 files by "`parallel --number-of-cores`" jobs):"
function move_files {
  for file in "$@"; do
    echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
    mkdir -p "$destination"/`dirname "$file"`
    \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
  done
}
export -f move_files
export destination
find "$soure" -type f "-$days" -print0 | parallel -0 -n 10 move_files

PS: Du hast einen Tippfehler, "soure" sollte "source" sein. Ich habe den Variablennamen beibehalten.

Bogdan Velcea
quelle
0

Dies ist weniger elegant, aber einfach, wenn die Anzahl / Größe der Dateien nicht zu groß ist

Zippen Sie Ihre Dateien zusammen in ein zipArchiv und entpacken Sie sie dann am Ziel ohne die -jOption. Standardmäßig erstellt zip die relative Verzeichnisstruktur.

Chris Johnson
quelle
0

Versuchen Sie diesen Weg:

IFS=$'\n'
for f in `find "$soure" -type f -mtime "-$days"`;
do
  mkdir -p "$destination"/`dirname $f`;
  mv $f "$destination"/`dirname $f`;
done
Alessio
quelle
0

Da es anscheinend keine wirklich einfache Lösung dafür gibt und ich sie sehr oft brauche, habe ich dieses Open Source-Dienstprogramm für Linux erstellt (erfordert Python): https://github.com/benapetr/smv

Es gibt mehrere Möglichkeiten, wie Sie damit das erreichen können, was Sie benötigen. Am einfachsten wäre jedoch Folgendes:

 # -vf = verbose + force (doesn't stop on errors)
smv -vf `find some_folder -type f -mtime "-$days"` target_folder

Sie können es auch im Trockenmodus ausführen, damit es nur das druckt, was es tun würde

smv -rvf `find some_folder -type f -mtime "-$days"` target_folder

Oder falls diese Dateiliste zu lang ist, um in eine Argumentationszeile zu passen, und es Ihnen nichts ausmacht, Python für jede einzelne Datei auszuführen

find "$soure" -type f -mtime "-$days" -exec smv {} "$destination" \;
Petr
quelle