替换文件时保持(或还原)文件权限


11

我有一个命令,该命令接受文件作为参数,修改文件,然后将其写入第二个参数中指定的文件名。我将调用该程序modifyfile

我希望它可以“就地”运行,所以我编写了一个shell脚本(bash),将其修改为一个临时文件,然后将其移回:

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

不幸的是,该文件的权限受到了破坏。使用默认权限重新创建该文件。

有没有办法告诉mv命令覆盖目标而不更改其权限?或者,是否有一种方法可以保存原始用户,组和权限并还原它们?

Answers:


10

不用mv重定向,只需重定向即可cat。例如:

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

这将覆盖$original的内容$TMP,而不会在文件级别触及任何内容。


如果某个程序的文件具有打开的文件句柄,这不会有问题吗?
Martin von Wittich

2
然后,我也必须这样rm "$TMP"做,但是它似乎可以满足我的要求。
Stephen Ostermiller 2013年

@MartinvonWittich如果您使用的话可能会是一个问题mv。我没有解决该问题的方法。
奋斗

2
@MartinvonWittich是的。Create-new-then-move给您带来了原子上的改变,并且不会影响打开该文件的程序,但是由于它创建了一个新文件,因此该文件的所有权和权限都会丢失。截断现有然后写入可保留权限和所有权,但在崩溃时会丢失数据,并在打开文件的程序下滑动地毯。您不能将两者的优点结合在一起。
吉尔(Gilles)“所以,别再邪恶了”

1
@MartinvonWittich chown仅作为根用户。chmod并且chgrp可能会或可能不会起作用,具体取决于用户的权限。都不复制其他属性,例如ACL或特定于文件系统的扩展属性。
吉尔(Gilles)'所以

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"

好答案!如今,我建议在方法1中cp命令使用--attributes-only参数。这样,将不会使用资源来复制文件的内容。我找不到从哪个版本添加此参数。cp -p --attributes-only "$original" "$tmp"
马塞洛·巴罗斯

@MarceloBarros它是2010年10月15日发布的GNU coreutils 8.6中所添加的,因此,如今,如果您拥有GNU coreutils,则应该拥有它。其他cp实现仍然没有这种东西。
吉尔斯

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我就不会丢弃它
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.