Unterbrechen Sie alle Hardlinks in einem Ordner

10

Ich habe einen Ordner, der eine bestimmte Anzahl von Dateien enthält, die feste Links haben (im selben Ordner oder an einem anderen Ort), und ich möchte die Verknüpfung dieser Dateien aufheben, damit sie unabhängig werden und Änderungen an ihrem Inhalt keine Auswirkungen haben andere Datei (ihre Linkanzahl wird 1).

Im Folgenden gebe ich eine Lösung an, bei der jeder feste Link an einen anderen Ort kopiert und dann wieder an seinen Platz verschoben wird.

Diese Methode scheint jedoch ziemlich grob und fehleranfällig zu sein, daher würde ich gerne wissen, ob es einen Befehl gibt, der die Verknüpfung einer Datei für mich aufhebt.

Grobe Antwort:

Suchen Sie nach Dateien mit Hardlinks ( Bearbeiten : Um auch Sockets usw. mit Hardlinks zu finden, verwenden Sie find -not -type d -links +1):

find      -type f -links +1 # files only
find -not -type d -links +1 # files, sockets etc.

Eine grobe Methode, um die Verknüpfung einer Datei aufzuheben (sie an einen anderen Speicherort zu kopieren und zurückzuschieben): Bearbeiten: Wie Celada sagte, ist es am besten, unten ein cp -p auszuführen, um zu vermeiden, dass Zeitstempel und Berechtigungen verloren gehen. Bearbeiten: Erstellen Sie ein temporäres Verzeichnis und kopieren Sie es in eine Datei darunter. Anstatt eine temporäre Datei zu überschreiben, wird das Risiko des Überschreibens einiger Daten minimiert, obwohl der mvBefehl immer noch riskant ist (danke @Tobu). Bearbeiten: Versuchen Sie, das temporäre Verzeichnis im selben Dateisystem (@MikkoRantalainen) zu erstellen.

# This is unhardlink.sh
set -e
for i in "$@"; do
  temp="$(mktemp -d -- "${i%/*}/hardlnk-XXXXXXXX")"
  [ -e "$temp" ] && cp -ip "$i" "$temp/tempcopy" && mv "$temp/tempcopy" "$i" && rmdir "$temp"
done

So lösen Sie die Verknüpfung aller Hardlinks ( Bearbeiten : geändert -type fin -not -type d, siehe oben):

find -not -type d -links +1 -print0 | xargs -0 unhardlink.sh
Suzanne Dupéron
quelle
Ich würde das nicht als "roh" betrachten. Die einzige Möglichkeit, dies schneller zu erreichen, besteht wahrscheinlich darin, mit dem Systemaufruf sendfile () einen Trick auszuführen, die Verknüpfung der Open Source-Datei aufzuheben und das Ziel direkt neu zu schreiben. Ehrlich gesagt ist es die Mühe nicht wert.
Matthew Ife
Mit 'roh' meine ich, dass ich zum Beispiel, als ich diesen Befehl mit dem cp -iSchalter ausführte, ein paar Nachrichten an mich spuckte, in denen ich gefragt wurde, ob er ./fileXXXXXX(die $tempDatei) überschreiben soll, obwohl tmpfile eindeutige Dateinamen geben sollte, also muss es eine Art Rennbedingung oder was auch immer sein, und damit das Risiko, einige Daten zu verlieren.
Suzanne Dupéron
1
Es ist normal, dass die Datei vorhanden ist. Sie haben sie nur mit tempfile erstellt (nb: veraltet zugunsten von mktemp, aber das hat Ihr Problem nicht verursacht).
Tobu
1
Sie unhardlink.shsollten ein temporäres Verzeichnis in demselben Verzeichnis erstellen, das die Datei enthält, die nicht verknüpft werden muss. Andernfalls wird Ihr rekursiver Aufruf möglicherweise in einem anderen Dateisystem wiederholt, und Sie verschieben Inhalte über Dateisystemgrenzen hinweg, da sich Ihr temporäres Verzeichnis im aktuellen Arbeitsverzeichnis befindet. Ich denke, Sie könnten "$(dirname "$i")/hardlink-XXXXXX"stattdessen als Argument an mktemp übergeben.
Mikko Rantalainen
1
@MikkoRantalainen Vielen Dank, aktualisiert! Beachten Sie, dass das Dateisystem, wenn es sich um eine Art UnionFS oder ein fuseDateisystem handelt, möglicherweise tatsächlich path/to/hardlink-XXXauf ein anderes physisches Speichermedium als gesendet wird path/to/original-file, aber dagegen kann nicht viel getan werden.
Suzanne Dupéron

Antworten:

9

In Ihrem Skript gibt es Raum für Verbesserungen, z. B. das Hinzufügen einer -pOption zum cpBefehl, damit Berechtigungen und Zeitstempel während des Unhardlink-Vorgangs erhalten bleiben, und Sie können eine Fehlerbehandlung hinzufügen, damit die temporäre Datei im Fehlerfall gelöscht wird. Die Grundidee Ihrer Lösung ist jedoch die einzige, die funktioniert. Um die Verknüpfung einer Datei aufzuheben, müssen Sie sie kopieren und dann wieder über den ursprünglichen Namen verschieben. Es gibt keine "weniger grobe" Lösung, und diese Lösung hat Race-Bedingungen für den Fall, dass ein anderer Prozess gleichzeitig auf die Datei zugreift.

Celada
quelle
Tatsächlich verwende ich beim Kopieren immer cp -a, um alles zu erhalten, Symlinks als Symlinks zu rekursieren und zu kopieren. Ich weiß nicht, warum ich es dieses Mal vergessen habe, aber nachdem ich Ihre Antwort gesehen hatte, verstand ich, dass ich alle meine Zeitstempel vermasselt hatte und sie (ziemlich schmerzhaft) von einem Backup wiederherstellen musste.
Suzanne Dupéron
5

Wenn Sie Speicherplatz verbrennen möchten und eine relativ moderne Version von tar(z. B. Ubuntu 10.04 und CentOS 6) haben, können Sie mit dieser --hard-dereferenceOption spielen.

Etwas wie:

$ cd /path/to/directory
$ ls -l *
bar:
total 12
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 1
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 3

foo:
total 12
-rw-rw-r-- 2 cjc cjc 3 May  6 19:07 1
-rw-rw-r-- 2 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 4

(wo ich gelaufen war ln foo/[12] bar)

$ tar cvf /tmp/dereferencing.tar --hard-dereference .
$ tar xvf /tmp/dereferencing.tar
$ ls -l *
bar:
total 12
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 1
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 3

foo:
total 12
-rw-rw-r-- 1 cjc cjc 3 May  6 19:07 1
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 2
-rw-rw-r-- 1 cjc cjc 2 May  6 19:07 4

Von der Manpage:

   --hard-dereference
          follow hard links; archive and dump the files they refer to
cjc
quelle
Ich vermute, es gibt wenig, was Teer nicht kann. Schöne Lösung.
Joseph Kern
Ich habe vergessen zu erwähnen, dass ich nicht genug Speicherplatz hatte, um alles zu kopieren. Grundsätzlich ist Ihre Methode dieselbe cp -a --no-preserve=links /path/to/folder /path/to/copy && rm -rf /path/to/folder && mv /path/to/copy /path/to/folder, wenn ich mich nicht irre. Ich denke, Ihre Methode wäre jedoch effizienter, da Teer weniger Festplattensuchen und damit weniger Thrashing beinhalten würde. Das gleiche könnte man mit rsync erreichen, mit noch geringerer Leistung als mit der cp-Methode :).
Suzanne Dupéron
1
Um zu vermeiden, dass viel zusätzliche Festplatte verwendet wird, kann möglicherweise so etwas ausgeführt werden tar cvf - --hard-dereference . | tar xf -, es kann jedoch eine Race-Bedingung vorliegen, die dazu führt, dass Dinge explodieren. Ich habe es nicht versucht, und ich bin im Moment irgendwie nicht geneigt, dies zu tun.
cjc