sed -i 触摸它不会改变的文件 [英] sed -i touching files that it doesn't change

查看:23
本文介绍了sed -i 触摸它不会改变的文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们服务器上有人运行了 sed -i 's/$var >>$var2/$var >$var2/* 将插入更改为在公共目录中的某些 bash 脚本中覆盖.没什么大不了的,它首先用 grep 进行了测试,它返回了预期的结果,只有他的文件会被触及.

Someone on our server ran sed -i 's/$var >> $var2/$var > $var2/ * to change inserts to overwrites in some bash scripts in a common directory. No big deal, it was tested first with grep and it returned the expected results that only his files would be touched.

他运行了脚本,现在文件夹中 1400 个文件中的 1200 个文件有了新的修改日期,但据我们所知,实际上只有他的一小部分文件被更改了.

He ran the script and now 1200 files of the 1400 in the folder have a new modified date, yet as far as we can tell, only his small handful of files were actually changed.

  1. 为什么 sed 会触摸"一个没有改变的文件.
  2. 为什么它只会触及"文件的一部分而不是全部.
  3. 它是否真的改变了一些东西(可能是一些尾随的空格或一些完全出乎意料的东西,因为 sed 正则表达式中的 $ )?

推荐答案

当 GNU sed 成功就地"编辑文件时,其时间戳会更新.要了解原因,让我们回顾一下就地"编辑是如何完成的:

When GNU sed successfully edits a file "in-place," its timestamp is updated. To understand why, let's review how edit "in-place" is done:

  1. 创建一个临时文件来保存输出.

  1. A temporary file is created to hold the output.

sed 处理输入文件,将输出发送到临时文件.

sed processes the input file, sending output to the temporary file.

如果指定了备份文件扩展名,则输入文件将重命名为备份文件.

If a backup file extension was specified, the input file is renamed to the backup file.

无论是否创建备份,都会移动临时输出(rename) 到输入文件.

Whether a backup is created or not, the temporary output is moved (rename) to the input file.

GNU sed 不会跟踪是否对文件进行了任何更改.临时输出文件中的任何内容都通过 rename 移动到输入文件中.

GNU sed does not track whether any changes were made to the file. Whatever is in the temporary output file is moved to the input file via rename.

这个过程有一个很好的好处:POSIX 要求 重命名是原子的.因此,输入文件永远不会处于损坏状态:它要么是原始文件,要么是修改后的文件,而且永远不会介于两者之间.

There is a nice benefit to this procedure: POSIX requires that rename be atomic. Consequently, the input file is never in a mangled state: it is either the original file or the modified file and never part way in-between.

作为此过程的结果,sed 成功处理的任何文件都将更改其时间戳.

As a result of this procedure, any file that sed successfully processes will have its timestamp changed.

让我们考虑一下这个inputfile:

$ cat inputfile
this is
a test.

现在,在 strace 的监督下,让我们在其上运行 sed -i 保证不会引起任何更改:

Now, under the supervision of strace, let's run sed -i on it in a way guaranteed to cause no changes:

$ strace sed -i 's/XXX/YYY/' inputfile

编辑后的结果如下:

execve("/bin/sed", ["sed", "-i", "s/XXX/YYY/", "inputfile"], [/* 55 vars */]) = 0
[...snip...]
open("inputfile", O_RDONLY)             = 4
[...snip...]
open("./sediWWqLI", O_RDWR|O_CREAT|O_EXCL, 0600) = 6
[...snip...]
read(4, "this is
a test.
", 4096)     = 16
write(6, "this is
", 8)                = 8
write(6, "a test.
", 8)                = 8
read(4, "", 4096)                       = 0
[...snip...]
close(4)                                = 0
[...snip...]
close(6)                                = 0
[...snip...]
rename("./sediWWqLI", "inputfile")      = 0

如您所见,sed 在文件句柄 4 上打开输入文件 inputfile.然后它会创建一个临时文件 ./sediWWqLI 在文件句柄 6 上,用于保存输出.它从输入文件中读取并将其写入输出文件.完成后,将调用 rename 以覆盖 inputfile,更改其时间戳.

As you can see, sed opens the input file, inputfile, on file handle 4. It then creates a temporary file, ./sediWWqLI on file handle 6, to hold the output. It reads from the input file and writes it unchanged to the output file. When this is done, a call to rename is made to overwrite inputfile, changing its timestamp.

相关源码在源码.从 4.2.1 版开始:

The relevant source code is in the execute.c file of the sed directory of the source. From version 4.2.1:

  ck_fclose (input->fp);
  ck_fclose (output_file.fp);
  if (strcmp(in_place_extension, "*") != 0)
    {
      char *backup_file_name = get_backup_file_name(target_name);
      ck_rename (target_name, backup_file_name, input->out_file_name);
      free (backup_file_name);
    }

  ck_rename (input->out_file_name, target_name, input->out_file_name);
  free (input->out_file_name);

ck_rename 是 stdio 函数 rename 的覆盖函数.ck_rename 的源代码在 sed/utils.c 中.

ck_rename is a cover function for the stdio function rename. The source for ck_rename is in sed/utils.c.

如您所见,没有保留标志来确定文件是否实际更改.rename 无论如何都会被调用.

As you can see, no flag is kept to determine whether the file actually changed or not. rename is called regardless.

至于时间戳未更改的 1400 个文件中的 200 个,这意味着 sed 以某种方式在这些文件上失败了.一种可能是权限问题.

As for the 200 of the 1400 files whose timestamps did not change, that would mean that sed somehow failed on those files. One possibility would be a permissions issue.

正如 mklement0 所述,将 sed -i 应用于符号链接会导致令人惊讶的结果.sed -i 不更新符号链接指向的文件.相反,sed -i 用新的常规文件覆盖符号链接.

As noted by mklement0, applying sed -i to a symbolic link leads to a surprising result. sed -i does not update the file pointed to by the symbolic link. Instead, sed -i overwrites the symbolic link with a new regular file.

这是sed 对STDIO rename 进行调用的结果.正如 man 2 rename 所记录:

This is a result of the call that sed makes to the STDIO rename. As documented by man 2 rename:

如果 newpath 引用了符号链接,则链接将被覆盖.

if newpath refers to a symbolic link the link will be overwritten.

mklement0 报告说,Mac OSX 10.10 上的 (BSD) sed 也是如此.

mklement0 reports that this is also true of the (BSD) sed on Mac OSX 10.10.

这篇关于sed -i 触摸它不会改变的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆