在此bash 脚本示例页面作者给出了如下脚本:
# Cleanup
# Run as root, of course.
cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Log files cleaned up."
你为什么cat /dev/null
要做任何事情?我不明白这里的意思(这是否像使用while TRUE; sleep 1; elihw
for一样{some busy program}
?)。然而作者称其为“没什么不寻常的”。
答案1
通常情况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 脚本的整个概念:它只是一组简单的命令,可以轻松地从命令行运行,但放在文本文件中,以避免一遍又一遍地重新输入它们。
答案2
为什么要将 cat /dev/null 放到任何东西上?
这样做是为了截断文件内容,同时保持 inode 完好无损。除了文件大小将重置为零之外,所有打开该文件进行读写的程序都不会受到影响。
常见的一种虚假替代方法是删除该文件然后再次创建它:
rm file
touch file
或类似内容:
mv file file.old
gzip file.old
touch file
问题是这些方法不能阻止在删除时打开已删除文件的任何进程继续写入旧文件。原因是在 Unix 文件系统下,当您删除文件时,您只会将其名称(路径)与其内容(inode)断开链接。只要有进程打开它进行读取或写入,inode 就会保持活动状态。
这会导致几个负面影响:由于没有直接/可移植的方式来打开已删除的文件,因此文件删除后写入的日志会丢失。只要有进程正在写入已删除的文件,其内容仍在使用文件系统上的空间。这意味着,如果您因为文件填满而删除/创建该文件,则磁盘将保持填满状态。解决后一个问题的一种方法是重新启动记录器进程,但您可能不想对关键服务执行此操作,否则中间日志将彻底丢失。由于您创建的文件可能不具有与原始文件相同的权限、所有者和组,因此还会产生副作用。例如,这可能会阻止日志分析器读取新创建的文件,或者更糟的是,阻止日志记录过程写入自己的日志。
第一种方法,cat /dev/null > file
正确实现目标然而,尽管这是一个经久不衰的都市传说,但它的cat /dev/null
作用却毫无用处。它会打开一个设计为空的伪文件,无法从中读取任何内容,最终直接退出。使用此命令会浪费按键、字节、系统调用和 CPU 周期,并且可以用无疑更快的无操作命令替换它,而无需进行任何功能更改:
,对于大多数 shell 来说,甚至可以不执行任何命令。
让我打个比方来解释一下无用这个词cat /dev/null
的含义。假设你的目标是喝完一杯水。
首先,您要清除其中的所有液体。这就足够了,而且这正是 (
> file
) 所做的,因为重定向总是首先被处理。然后,你拿一个空瓶子(
/dev/null
)并将水倒入空玻璃杯中(cat
)。这是毫无意义的步骤……
如果你读您的链接文档到最后,您可能会注意到增强版脚本中此行的注释:
cat /dev/null > wtmp # ':> wtmp' 和 '> wtmp' 具有相同的效果。
确实如此;但cat /dev/null
保留在代码中太糟糕了。
这意味着以下代码将适用于所有常见的 shell(csh
和sh
系列):
cd /var/log
: > messages
: > wtmp
echo "Log files cleaned up."
并且这将适用于所有使用 Bourne 语法的 shell,例如ash
,,,bash
等等:ksh
zsh
cd /var/log
> messages
> wtmp
echo "Log files cleaned up."
但请注意,对于古老的、POSIX 之前的 Bourne shell,cat /dev/null
如果文件之后由仍在运行的 shell 脚本附加到其上,则任何这些命令(包括)都不会截断文件。这将不是一个零字节文件,而是一个大小不变的稀疏文件。如果文件是由在写入之前寻找它认为是当前位置的进程写入的,也会发生同样的情况。
还要注意,一些经常建议的截断文件的替代解决方案确实存在缺陷。
以下两种方法都不起作用。生成的文件不为空,但包含一个空行。这会破坏
wtmp
存储固定宽度记录的日志文件。echo > file echo "" > file
下一个基于 BSD
sh
选项的版本是不可移植的,POSIX 没有为 echo 指定任何允许的选项,因此您最终可能会得到一个包含“ ”行的文件-n
:echo -n > file
通过使用 System V 转义序列,该命令无法移植
sh
。某些 shell 将创建一个包含“\c
”行的文件:echo "\c" > file
那个命令使用专门用于完成该任务的命令。问题是使用
truncate
不可移植,因为这个命令不是由 POSIX 指定的,Unix/Linux 系统可能会缺少它。truncate -s 0
最后,这里有一些可移植且能正确完成工作的替代方案:
明确地将空字符串打印到文件中:
printf "" > file
使用
true
与无操作命令严格等效:
但更易读的命令:true > file
答案3
将文件大小归零是一种很麻烦的方法。
cd /var/log
> messages
> wtmp
echo "Log files cleaned up."
答案4
截断打开的文件。以下代码等效且更易于理解:
echo -n > /var/log/messages
(添加 -n 以避免换行)