Zachowaj (lub przywróć) uprawnienia do pliku podczas zastępowania pliku

11

Mam polecenie, które akceptuje plik jako argument, modyfikuje plik, a następnie zapisuje go do nazwy pliku określonej w drugim argumencie. Zadzwonię do tego programu modifyfile.

Chciałem, żeby działał „na miejscu”, więc napisałem skrypt powłoki (bash), który modyfikuje go do pliku tymczasowego, a następnie przenosi go z powrotem:

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

Ma to niefortunny efekt uboczny niszczenia uprawnień do tego pliku. Plik zostanie ponownie utworzony z domyślnymi uprawnieniami.

Czy istnieje sposób na mvpolecenie polecenia zastąpienia miejsca docelowego bez zmiany jego uprawnień? Czy alternatywnie istnieje sposób na uratowanie użytkownika, grupy i uprawnień od oryginału i przywrócenie ich?

Stephen Ostermiller
źródło

Odpowiedzi:

10

Zamiast używać mv, po prostu przekieruj cat. Na przykład:

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

To nadpisuje $originalzawartość $TMP, nie dotykając niczego na poziomie pliku.

strugee
źródło
Czy to nie może być problematyczne, jeśli jakiś program ma otwarty uchwyt pliku do pliku?
Martin von Wittich,
2
W takim razie muszę to zrobić rm "$TMP", ale wydaje się, że robię to, co chcę.
Stephen Ostermiller
@MartinvonWittich prawdopodobnie byłby to problem, gdybyś używał mvzamiast tego. Nie widzę sposobu na rozwiązanie tego problemu.
strugee
2
@MartinvonWittich Tak. Utwórz-nowy-wtedy-przenieś daje atomową zmianę i nie wpływa na programy, które mają otwarty plik, ale ponieważ tworzy nowy plik, własność pliku i uprawnienia są tracone. Truncate-istniejący-wtedy-zapis zachowuje uprawnienia i własność, ale traci dane w przypadku awarii i przesuwa dywan pod stopami programów, które mają otwarty plik. Nie można łączyć dobrych części obu.
Gilles „SO- przestań być zły”
1
@MartinvonWittich chowndziała tylko jako root. chmodi chgrpmoże, ale nie musi, działać w zależności od uprawnień użytkownika. Nie kopiuje także innych atrybutów, takich jak ACL lub atrybuty rozszerzone specyficzne dla systemu plików.
Gilles „SO- przestań być zły”
10

Istnieją dwie strategie zastępowania pliku nową wersją:

  1. Utwórz plik tymczasowy w nowej wersji, a następnie przenieś go na miejsce.

    • Zaleta: jeśli program otworzy ten plik, albo odczyta starą, albo nową zawartość, w zależności od tego, czy otworzył plik przed czy po przeniesieniu. Nie ma pomyłki.
    • Zaleta: w przypadku awarii stara treść zostaje zachowana.
    • Wada: ponieważ tworzony jest nowy plik, atrybuty pliku (własność, pozwolenie itp.) Nie są zachowywane.
  2. Zastąp stary plik na miejscu.

    • Zaleta: atrybuty pliku są zachowane.
    • Wada: w przypadku awarii plik może zostać w połowie zapisany.
    • Wada: jeśli program ma otwarty plik podczas aktualizacji, ten program może odczytać niespójne dane.

Jeśli możesz, użyj metody 1, ale najpierw zreplikuj atrybuty oryginalnego pliku za pomocą cp -p --attributes-only. Wymaga to GNU coreutils (tj. Niewbudowanego systemu Linux lub środowisk wystarczająco podobnych do systemu Linux). Jeśli cpnie masz --attributes-only, pomiń tę opcję: będzie działać, ale również powiela dane.

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

Jeśli nie możesz replikować atrybutów istniejącego pliku, na przykład ponieważ masz do niego uprawnienia do zapisu, ale nie jesteś jego właścicielem i chcesz zachować właściciela, to możliwa jest tylko metoda 2. Aby zminimalizować ryzyko utraty danych:

  • Uczyń okno, w którym plik będzie niekompletny, tak małe, jak to możliwe. Najpierw przygotuj dane w pliku tymczasowym, a następnie skopiuj na miejsce.
  • Najpierw wykonaj kopię zapasową starego pliku.

tmp=$(mktemp)
backup="${original}~"
modifyfile "$original" "$tmp"
cp -p "$original" "$backup"
cp -f "$tmp" "$original"
Gilles „SO- przestań być zły”
źródło
Niezła odpowiedź! Obecnie sugeruję użycie argumentu --attributes-only z komendą cp w Metodzie 1 . W ten sposób cp -p --attributes-only "$original" "$tmp"nie będą używać zasobów do kopiowania zawartości pliku. Nie mogłem znaleźć, z której wersji ten argument został dodany.
Marcelo Barros
@MarceloBarros Został on dodany w GNU coreutils 8.6 wydany 15.10.2010, więc teraz, jeśli masz GNU coreutils, powinieneś go mieć. Nadal nie ma czegoś takiego w przypadku innych cpimplementacji.
Gilles „SO- przestań być zły”
5

Po naszej dyskusji na temat pierwszej odpowiedzi proponuję inną odpowiedź:

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

Uwagi:

  • Używam $originalw mktempszablonie, aby upewnić się, że plik tymczasowy nie jest umieszczony /tmpw tym samym folderze co $original. Uważam, że jeśli /tmpjest zamontowany na innym systemie plików, operacja nie byłaby już atomowa.
  • Wynik mktempjest teraz cytowany, jeśli zawiera spacje.
  • Używam $()zamiast `` ponieważ uważam to za czystsze.
  • ch{mod,own} --referencesłużą do przeniesienia uprawnień $originaldo $TMP. Jeśli ktoś ma dodatkowe pomysły na to, jakie metadane mogą i powinny zostać przeniesione, edytuj mój post i dodaj go.
  • No cóż, to wymaga uprawnień roota, jak zauważył Gilles. Cóż, nie odrzucę tego teraz, kiedy to napisałem: P.
Martin von Wittich
źródło