我有一个命令接受文件作为参数,修改该文件,然后将其写入第二个参数中指定的文件名。我将调用该程序modifyfile
。
我希望它“就地”工作,所以我编写了一个 shell 脚本 (bash),将其修改为临时文件,然后将其移回:
TMP=`mktemp`
modifyfile "$original" "$TMP"
mv -v "$TMP" "$original"
不幸的是,这会破坏该文件的权限。该文件将使用默认权限重新创建。
有没有办法告诉mv
命令覆盖目标而不改变其权限?或者有没有办法保存原始用户、组和权限并恢复它们?
答案1
不使用mv
,只需重定向cat
。例如:
TMP=$(mktemp)
modifyfile "$original" "$TMP"
cat "$TMP" > "$original"
这将覆盖$original
的内容$TMP
,而不触及文件级别的任何内容。
答案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"
答案3
在我们讨论第一个答案之后,我提出了一个不同的答案:
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
。如果有人有其他想法可以并且应该传输哪些元数据,请编辑我的帖子并添加它。- 哦,正如吉尔斯指出的那样,这需要 root 权限。好吧,既然我已经写了它,我就不会放弃它:P