ファイルを置き換えるときにファイルのアクセス許可を維持(または復元)する


11

ファイルを引数として受け入れ、ファイルを変更してから、2番目の引数で指定されたファイル名に書き込むコマンドがあります。私はそのプログラムを呼びますmodifyfile

「そのまま」動作するようにしたいので、一時ファイルに変更してから元に戻すシェルスクリプト(bash)を作成しました。

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

これには、このファイルの権限を破壊するという不幸な副作用があります。ファイルはデフォルトの権限で再作成されます。

mv権限を変更せずに宛先を上書きするようにコマンドに指示する方法はありますか?または、元のユーザー、グループ、および権限を保存して復元する方法はありますか?

回答:


10

を使用する代わりにmv、リダイレクトするだけcatです。例えば:

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

これは、ファイルレベルで何も変更せずに$original、の内容で上書きし$TMPます。


あるプログラムがファイルへのオープンファイルハンドルを持っている場合、それは問題ではないでしょうか?
マーティンフォンヴィッティヒ2013

2
それから私rm "$TMP"もそうしなければなりませんが、それは私が望んでいることだけをしているようです。
Stephen Ostermiller 2013

@MartinvonWittich mv代わりに使用している場合はおそらく問題になります。その問題を解決する方法がわかりません。
2013

2
@MartinvonWittichはい。create-new-then-moveはアトミックな変更を提供し、ファイルを開いているプログラムには影響しませんが、新しいファイルを作成するため、ファイルの所有権とアクセス許可は失われます。Truncate-existing-then-writeはアクセス許可と所有権を保持しますが、クラッシュの場合はデータを失い、ファイルを開いているプログラムの足元に敷物を拭きます。両方の長所を組み合わせることはできません。
Gilles「SO-邪悪になるのをやめる」

1
@MartinvonWittich chownはrootとしてのみ機能します。chmodまたchgrp、ユーザーの権限に応じて機能する場合と機能しない場合があります。どちらも、ACLやファイルシステム固有の拡張属性などの他の属性をコピーしません。
Gilles「SO-邪悪なことをやめなさい」

10

ファイルを新しいバージョンで置き換えるには、2つの方法があります。

  1. 新しいバージョンで一時ファイルを作成し、適切な場所に移動します。

    • 利点:プログラムがそのファイルを開く場合、移動の前または後にファイルを開いたかどうかに応じて、古いコンテンツまたは新しいコンテンツを読み取ります。取り違えはありません。
    • 利点:クラッシュした場合、古いコンテンツが保持されます。
    • 欠点:新しいファイルが作成されるため、ファイルの属性(所有権、権限など)は保持されません。
  2. 古いファイルを上書きします。

    • 利点:ファイルの属性が保持されます。
    • 欠点:クラッシュした場合、ファイルが半分書き込まれたままになることがあります。
    • 欠点:更新中にプログラムがファイルを開いていると、このプログラムは矛盾したデータを読み取る可能性があります。

可能であれば、方法1を使用しますが、最初に元のファイルの属性をで複製しますcp -p --attributes-only。これには、GNU coreutils(つまり、非組み込みLinux、または十分にLinuxに似た環境)が必要です。cpがない場合は--attributes-only、このオプションを省略します。機能しますが、データも複製されます。

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

既存のファイルの属性をレプリケートできない場合(たとえば、ファイルへの書き込み権限はあるが所有しておらず、所有者を保持したい場合)は、方法2のみが可能です。データ損失のリスクを最小限に抑えるには:

  • ファイルが不完全になるウィンドウをできるだけ小さくします。最初に一時ファイルでデータを準備してから、所定の場所にコピーします。
  • 最初に古いファイルのバックアップを作成します。

tmp=$(mktemp)
backup="${original}~"
modifyfile "$original" "$tmp"
cp -p "$original" "$backup"
cp -f "$tmp" "$original"

素敵な答え!最近では、方法1のcpコマンドで引数--attributes-onlyを使用することをお勧めします。このように、はリソースを使用してファイルの内容をコピーしません。この引数が追加されたバージョンを見つけることができませんでした。cp -p --attributes-only "$original" "$tmp"
Marcelo Barros

@MarceloBarros 2010-10-15にリリースされたGNU coreutils 8.6で追加されたため、最近では、GNU coreutilsがあれば、それも必要です。他のcp実装ではまだそのようなことはありません。
Gilles「SO-邪悪なことをやめ

5

最初の回答について話し合った後、別の回答を提案します。

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

備考:

  • テンプレートで使用$originalmktempて、一時ファイルが/tmpと同じフォルダに配置されないようにします$original/tmp別のファイルシステムにマウントされている場合、操作はアトミックではなくなると思います。
  • の結果mktempが空白を含む場合に備えて、引用されます。
  • $()綺麗だと思ったので「」の代わりに使っています。
  • ch{mod,own} --reference権限を転送するために使用されている$original$TMP。メタデータで転送できる、また転送すべきであるというアイデアが他にある場合は、投稿を編集して追加してください。
  • ああ、Gillesが指摘したように、これにはroot権限が必要です。まあ、私はこれを書いたのでこれを破棄するつもりはありません:P
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.