Сохранять (или восстанавливать) права доступа к файлу при замене файла

11

У меня есть команда, которая принимает файл в качестве аргумента, изменяет файл, а затем записывает его в имя файла, указанное во втором аргументе. Я позвоню этой программе 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, не затрагивая ничего на уровне файла.

strugee
источник
Разве это не может быть проблематично, если какая-то программа имеет дескриптор открытого файла?
Мартин фон Виттих
2
Тогда мне тоже нужно rm "$TMP", но, похоже, я делаю то, что хочу.
Стивен Остермиллер,
@MartinvonWittich, вероятно, было бы проблемой, если бы вы использовали mvвместо этого. Я не вижу способа решить эту проблему.
стружка
2
@MartinvonWittich Да. Create-new-then-move дает вам атомарное изменение и не влияет на программы, у которых файл открыт, но, поскольку он создает новый файл, владелец файла и права доступа теряются. Truncate-существующие-then-write сохраняет права и владелец, но теряет данные в случае сбоя и сильно удаляет ковер под ногами программ, у которых файл открыт. Вы не можете объединить хорошие части обоих.
Жиль "ТАК - перестать быть злым"
1
@MartinvonWittich chownработает только как root. chmodи chgrpможет или не может работать в зависимости от разрешений пользователя. Ни один из них не копирует другие атрибуты, такие как ACL или расширенные атрибуты файловой системы.
Жиль "ТАК - перестань быть злым"
10

Существует две стратегии замены файла новой версией:

  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"
Жиль "ТАК - перестань быть злым"
источник
Хороший ответ! В настоящее время я бы предложил использовать аргумент --attributes-only с командой cp в методе 1 . Таким образом, cp -p --attributes-only "$original" "$tmp"не будут использовать ресурсы для копирования содержимого файла. Я не мог найти, с какой версии этот аргумент был добавлен.
Марсело Баррос
@MarceloBarros Он был добавлен в GNU coreutils 8.6, выпущенной 2010-10-15, поэтому в наши дни, если у вас есть GNU coreutils, вы должны его иметь. С другими cpреализациями такого ещё нет .
Жиль "ТАК - перестать быть злым"
5

После нашего обсуждения первого ответа я предлагаю другой ответ:

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

Примечания:

  • Я использую $originalв mktempшаблоне, чтобы убедиться, что временный файл находится не в, /tmpа в той же папке, что и $original. Я считаю, что если /tmpсмонтирована на другой файловой системе, операция больше не будет атомарной.
  • Результат mktempтеперь указывается в случае, если он содержит пробелы.
  • Я использую $()вместо ``, потому что я считаю это чище.
  • ch{mod,own} --referenceиспользуются для передачи разрешений $originalна $TMP. Если у кого-то есть дополнительные идеи, какие метаданные можно и нужно передавать, отредактируйте мой пост и добавьте его.
  • Ну что ж, для этого нужны права суперпользователя, как указал Жиль. Ну, я не собираюсь отказываться от этого теперь, когда я написал это: P
Мартин фон Виттих
источник