Beibehalten (oder Wiederherstellen) von Dateiberechtigungen beim Ersetzen von Dateien

11

Ich habe einen Befehl, der eine Datei als Argument akzeptiert, die Datei ändert und sie dann in den im zweiten Argument angegebenen Dateinamen schreibt. Ich werde das Programm aufrufen modifyfile.

Ich wollte, dass es "an Ort und Stelle" funktioniert, also schrieb ich ein Shell-Skript (Bash), das es in eine temporäre Datei ändert und es dann zurück verschiebt:

TMP=`mktemp`
modifyfile "$original" "$TMP"
mv -v "$TMP" "$original"

Dies hat den unglücklichen Nebeneffekt, dass die Berechtigungen für diese Datei zerstört werden. Die Datei wird mit Standardberechtigungen neu erstellt.

Gibt es eine Möglichkeit, den mvBefehl anzuweisen, das Ziel zu überschreiben, ohne seine Berechtigungen zu ändern? Oder gibt es alternativ eine Möglichkeit, Benutzer, Gruppen und Berechtigungen aus dem Original zu speichern und wiederherzustellen?

Stephen Ostermiller
quelle

Antworten:

10

Anstatt zu verwenden mv, leiten Sie einfach um cat. Beispielsweise:

TMP=$(mktemp)
modifyfile "$original" "$TMP"
cat "$TMP" > "$original"

Dies überschreibt $originalmit dem Inhalt von $TMP, ohne etwas auf Dateiebene zu berühren.

strugee
quelle
Könnte das nicht problematisch sein, wenn ein Programm ein offenes Dateihandle für die Datei hat?
Martin von Wittich
2
Ich muss es dann rm "$TMP"auch, aber es scheint genau das zu tun, was ich will.
Stephen Ostermiller
@MartinvonWittich wäre es wahrscheinlich ein Problem, wenn Sie mvstattdessen verwenden würden. Ich sehe keinen Weg, um dieses Problem zu lösen.
Strugee
2
@ MartinvonWittich Ja. Create-new-then-move führt zu einer atomaren Änderung und wirkt sich nicht auf Programme aus, bei denen die Datei geöffnet ist. Da jedoch eine neue Datei erstellt wird, gehen der Besitz und die Berechtigungen der Datei verloren. Beim Abschneiden des vorhandenen Schreibvorgangs bleiben die Berechtigungen und das Eigentum erhalten, bei einem Absturz gehen jedoch Daten verloren, und der Teppich wird unter die Füße von Programmen gewischt, bei denen die Datei geöffnet ist. Sie können die guten Teile von beiden nicht kombinieren.
Gilles 'SO - hör auf böse zu sein'
1
@MartinvonWittich chownfunktioniert nur als root. chmodund chgrpkann abhängig von den Berechtigungen des Benutzers funktionieren oder nicht. Weder kopiert andere Attribute wie ACL oder dateisystemspezifische erweiterte Attribute.
Gilles 'SO - hör auf böse zu sein'
10

Es gibt zwei Strategien, um eine Datei durch eine neue Version zu ersetzen:

  1. Erstellen Sie eine temporäre Datei mit der neuen Version und verschieben Sie sie.

    • Vorteil: Wenn ein Programm diese Datei öffnet, liest es entweder den alten oder den neuen Inhalt, je nachdem, ob es die Datei vor oder nach dem Verschieben geöffnet hat. Es gibt keine Verwechslung.
    • Vorteil: Im Falle eines Absturzes bleibt der alte Inhalt erhalten.
    • Nachteil: Da eine neue Datei erstellt wird, bleiben die Attribute der Datei (Besitz, Berechtigung usw.) nicht erhalten.
  2. Überschreiben Sie die alte Datei.

    • Vorteil: Die Attribute der Datei bleiben erhalten.
    • Nachteil: Im Falle eines Absturzes kann die Datei halb geschrieben bleiben.
    • Nachteil: Wenn ein Programm die Datei während der Aktualisierung geöffnet hat, liest dieses Programm möglicherweise inkonsistente Daten.

Wenn Sie können, verwenden Sie Methode 1, replizieren Sie jedoch zuerst die Attribute der Originaldatei mit cp -p --attributes-only. Dies erfordert GNU-Coreutils (dh nicht eingebettetes Linux oder ausreichend Linux-ähnliche Umgebungen). Wenn cpdies nicht der Fall ist --attributes-only, lassen Sie diese Option aus: Es funktioniert, aber es werden auch die Daten repliziert.

tmp=$(mktemp)
cp -p --attributes-only "$original" "$tmp"
modifyfile "$original" "$tmp"
mv -f "$tmp" "$original"

Wenn Sie die Attribute der vorhandenen Datei nicht replizieren können, z. B. weil Sie über Schreibberechtigungen verfügen, diese aber nicht besitzen und den Eigentümer beibehalten möchten, ist nur Methode 2 möglich. So minimieren Sie das Risiko eines Datenverlusts:

  • Machen Sie das Fenster, in dem die Datei unvollständig sein wird, so klein wie möglich. Bereiten Sie die Daten zuerst in einer temporären Datei vor und kopieren Sie sie dann an ihren Platz.
  • Erstellen Sie zuerst eine Sicherungskopie der alten Datei.

tmp=$(mktemp)
backup="${original}~"
modifyfile "$original" "$tmp"
cp -p "$original" "$backup"
cp -f "$tmp" "$original"
Gilles 'SO - hör auf böse zu sein'
quelle
Gute Antwort! Heutzutage würde ich vorschlagen, das Argument --attributes-only mit dem Befehl cp in Methode 1 zu verwenden . Auf diese Weise cp -p --attributes-only "$original" "$tmp"werden keine Ressourcen zum Kopieren des Inhalts der Datei verwendet. Ich konnte nicht finden, ab welcher Version dieses Argument hinzugefügt wurde.
Marcelo Barros
@MarceloBarros Es wurde in GNU Coreutils 8.6 hinzugefügt, das am 15.10.2010 veröffentlicht wurde. Wenn Sie also heutzutage GNU Coreutils haben, sollten Sie es haben. Bei anderen cpImplementierungen gibt es so etwas noch nicht .
Gilles 'SO - hör auf böse zu sein'
5

Nach unserer Diskussion über die erste Antwort schlage ich eine andere Antwort vor:

TMP="$(mktemp "$original".XXXXXXXXXX)"
modifyfile "$original" "$TMP"
chmod --reference="$original" "$TMP"
chown --reference="$original" "$TMP"
mv -f "$TMP" "$original"

Bemerkungen:

  • Ich verwende $originalin der mktempVorlage, um sicherzustellen, dass die temporäre Datei nicht in, /tmpsondern im selben Ordner wie abgelegt wird $original. Ich glaube, wenn /tmpes auf einem anderen Dateisystem gemountet ist, wäre die Operation nicht mehr atomar.
  • Das Ergebnis von mktempwird jetzt angegeben, falls es Leerzeichen enthält.
  • Ich benutze $()anstelle von "", weil ich es für sauberer halte.
  • ch{mod,own} --referencewerden verwendet, um die Berechtigungen von an $originalzu übertragen $TMP. Wenn jemand zusätzliche Ideen hat, welche Metadaten übertragen werden können und sollen, dann bearbeite bitte meinen Beitrag und füge ihn hinzu.
  • Nun, dies erfordert Root-Berechtigungen, wie Gilles betonte. Nun, ich werde das jetzt, wo ich es geschrieben habe, nicht verwerfen: P.
Martin von Wittich
quelle