你为什么要`cat /dev/null > /var/log/messages`?

iso*_*mes 79 linux bash

在这个bash 脚本示例页面上,作者展示了这个脚本:

# Cleanup
# Run as root, of course.

cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Log files cleaned up."
Run Code Online (Sandbox Code Playgroud)

你为什么要cat /dev/null上任何东西?我不明白这里的意图是什么(就像使用while TRUE; sleep 1; elihwfor 一样{some busy program}?)。然而,作者称其为“没什么不寻常的”。

jll*_*gre 122

你为什么要把 /dev/null 放到任何东西上?

您将这样做以截断文件内容,同时保持 inode 完好无损。除了文件大小将重置为零之外,所有打开该文件进行读取或写入的程序都不会受到影响。

经常发现的一个虚假替代方法是删除文件然后再次创建它:

rm file
touch file
Run Code Online (Sandbox Code Playgroud)

或类似的:

mv file file.old
gzip file.old
touch file
Run Code Online (Sandbox Code Playgroud)

问题是这些方法不会阻止旧文件被在删除时打开已删除文件的任何进程写入。原因是在 Unix 文件系统下,当您删除一个文件时,您只是将其名称(路径)与其内容(inode)断开链接。只要有进程打开它进行读取或写入,inode 就会保持活动状态。

这会导致几个负面影响:文件删除后写入的日志丢失,因为没有直接/便携的方式来打开已删除的文件。只要进程正在写入已删除的文件,其内容仍会占用文件系统上的空间。这意味着,如果您删除/创建文件,因为它已填满您的磁盘,则该磁盘将保持填满状态。解决后一个问题的一种方法是重新启动记录器进程,但您可能不想为关键服务这样做,并且中间日志将最终丢失。由于您创建的文件可能与原始文件具有不同的权限、所有者和组,因此还有一些副作用。例如,这可能会阻止日志分析器读取新创建的文件,或者更糟的是,会阻止日志记录进程写入自己的日志。

第一种方法,cat /dev/null > file正确实现目标,尽管有顽强的都市传说,但它的cat /dev/null部分绝对没有任何用处。它打开一个设计为空的伪文件,它无法从中读取任何内容并最终退出。使用此命令会浪费击键、字节、系统调用和 CPU 周期,并且可以在没有任何功能更改的情况下被更快的 no-op 命令替换,:甚至在大多数 shell 中,根本不需要命令。

让我尝试一个比喻来解释无用cat /dev/null。假设您的目标是清空杯子。

  • 您首先从中取出任何液体。> file考虑到重定向总是首先处理的事实,这就足够了,这正是 ( ) 所做的。

  • 然后,您选择一个空瓶子 ( /dev/null) 并将其倒入空玻璃杯 ( cat) 中。这是毫无意义的一步……

如果您将链接的文档读到最后,您可能会注意到脚本增强版中这一行的注释:

cat /dev/null > wtmp #   ':> wtmp' 和 '> wtmp' 具有相同的效果。

他们确实有;太糟糕了cat /dev/null被保留在代码中。

这意味着,下面的代码将与所有普通弹(包括工作cshsh家庭):

cd /var/log
: > messages
: > wtmp
echo "Log files cleaned up."
Run Code Online (Sandbox Code Playgroud)

这将与使用Bourne语法所有炮弹一样工作ashbashkshzsh和喜欢:

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."
Run Code Online (Sandbox Code Playgroud)

但是请注意,对于古老的、POSIX 之前的 Bourne shell,这些命令中的任何cat /dev/null一个都不会截断文件,如果它是由一个仍在运行的 shell 脚本附加到它之后写入的。这将是一个大小不变的稀疏文件,而不是零字节文件。如果文件是由一个进程在写入之前寻求它认为是当前位置的位置写入的,则会发生同样的情况。

另请注意,通常建议截断文件的某些替代解决方案确实存在缺陷。

  • 以下两个都不能完成这项工作。生成的文件不是空的,而是包含一个空行。这会破坏wtmp存储固定宽度记录的日志文件。

     echo > file
     echo "" > file
    
    Run Code Online (Sandbox Code Playgroud)
  • 下一个基于 BSDsh选项的选项不可移植,POSIX 没有为 echo 指定任何允许的选项,因此您最终可能会得到一个包含带有“ -n”的行的文件:

     echo -n > file
    
    Run Code Online (Sandbox Code Playgroud)
  • 通过使用 System Vsh转义序列,那个也不能移植。一些 shell 会创建一个文件,其中包含一行“ \c”:

     echo "\c" > file
    
    Run Code Online (Sandbox Code Playgroud)
  • 那一个使用旨在完成这项工作的命令。问题是使用truncate不可移植,因为此命令不是由 POSIX 指定的,可能在 Unix/Linux 系统中丢失。

     truncate -s 0
    
    Run Code Online (Sandbox Code Playgroud)

最后,这里有几个可移植的替代方案,可以正确地完成工作:

  • 编写 `cat /dev/null` 的原因之一是为了明确你的意图。 (6认同)
  • @Davidmh 如果你真的想要在重定向之前明确的东西,我会推荐`printf "" > file`,它既是可移植的(POSIX)又是轻量级的,通常作为内置的 shell 实现。 (4认同)
  • @jlliagre 我对 shell 的了解非常基础,所以我可能不是最好的目标人群;但是光秃秃的 `>` 或 `:` 不清楚。我同意神话因其使用而传播是一种耻辱。 (2认同)

Gia*_*968 43

您通常cat /dev/null > [something]希望擦除文件内容,同时确保实际文件状态中断的风险绝对为零。文件的内容显然会被cat /dev/null文件本身擦除——因为它存在并且被它所在的文件系统知道——仍然会以相同的 inode 编号、所有权和权限存在。

在日志文件的情况下,日志文件本身可能被另一个进程标记为“正在使用”。例如,这样做rm /var/log/messages && touch /var/log/messages会破坏其他进程,并可能导致正在运行的进程阻塞。这意味着以某种方式锁定到连接到文件的特定 inode 编号的进程/var/log/messages可能会突然恐慌并说:“嘿!怎么了/var/log/messages!” 即使文件还在那里。更不用说错误地重新创建所有权和权限的潜在问题了。

由于文件使用/状态的这种不确定性cat /dev/null > [something],想要清除日志但不想潜在地干扰现有进程的操作的系统管理员更喜欢使用。

此外,在页面上下文中,您链接到作者的声明如下:

这里没有什么不寻常的,只有一组可以很容易地从控制台上的命令行或终端窗口中一个一个地调用的命令。将命令放置在脚本中的优势远不止不必一次又一次地重新键入它们。

因此,作者提到的“没什么不寻常的”是关于特定 bash 脚本的整个概念:它只是一组简单的命令,可以很容易地从命令行运行,但放在一个文本文件中避免必须一遍又一遍地重新键入它们。

  • @jlliagre 唯一“保持活力”的是问题的上下文,该问题的重点是原作者为什么会这样做。如果您热衷于消除“神话”,请发布一个答案,为原作者的编码方法提供上下文,以及为什么您认为它可以被其他方法取代的观点。 (10认同)
  • @jlliagre Cyrus 的回答没有解决原作者为什么会使用该方法的核心问题,也没有解决其背后的推理。提到的教程很旧,可以合理接受。这并没有错,也没有延续所谓的“都市传说”。相反,它是一个人编码的方式与另一个人编码的方式。就如此容易。这是一个对可靠性或性能没有负面影响的风格问题。 (7认同)
  • @skift `cat /dev/null > /foo/bar` 截断文件;`echo "" > /foo/bar` 将其截断,然后写入一个换行符。 (5认同)
  • `truncate -s 0` 会做同样的事情并且不那么惯用。然而,shell 程序员是一群保守的人,他们可能会遇到足够旧或足够古怪的系统,以至于缺少该命令。 (3认同)
  • 与 rm & touch 相比,此方法的另一个优点是保留了所有权和权限。 (2认同)

Cyr*_*rus 6

这是使文件大小为零的繁琐方法。

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."
Run Code Online (Sandbox Code Playgroud)

  • 正确的。该问题仅标记为“bash”。 (2认同)