:>filename.txt
例如:
root@box$ dd if=/dev/zero of=file.txt count=1024 bs=1024
1024+0 records in
1024+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00536175 s, 196 MB/s
root@box$ ll
total 1024
-rw-r--r-- 1 root root 1048576 Nov 15 14:40 file.txt
root@box$ :>file.txt
root@box$ ll
total 0
-rw-r--r-- 1 root root 0 Nov 15 14:40 file.txt
这与 不同吗rm
?与其他类似的清零或删除文件的方法相比,它的运行速度更快还是更慢?
答案1
正如您所发现的,这只是清空文件内容(它截断文件);这与rm
实际rm
完全删除文件不同。另外,:>file.txt
实际上会创造该文件(如果尚不存在)。
:
是一个“不执行任何操作的命令”,它将成功退出并且不产生任何输出,因此它只是清空文件的简短方法。在大多数 shell 中,您只需这样做即可>file.txt
获得相同的结果。它还可能比其他方法(例如可能是外部命令的方法)稍微快echo >file.txt
一些echo
。
此外,会在其中echo >file.txt
放置一个空行,使文件没有任何内容。file.txt
:>file.txt
答案2
是的,它与 不同rm
。
rm
将删除该文件。
:>filename.txt
清空文件,使其仍然存在,但大小为零字节。
答案3
shell 调用将>filename.txt
一些输出重定向到文件“filename.txt”以完全替换它。因此,shell 在将输出写入给定文件之前必须清除给定文件的所有内容。
要重定向的输出是执行命令的输出。喜欢:
$ echo Hello >filename.txt
将使名称为 filename.txt 的文件精确(且仅)包含 string Hello
。
执行:
$ echo "New Value" >filename.txt
$ cat filename.txt
New Value
将擦除文件内的所有内容,然后写入New Value
文件。
如果该命令没有输出(如命令)true
,则该文件将保持为空(被截断)。
$ true >filename.txt
$ cat filename.txt
也是 shell 内置命令的命令是:
(只是一个双点(冒号))并且没有输出。作为内置命令,它比外部命令true
(也没有输出)更快。所以,要么:
$ : > filename.txt
$ : >filename.txt
$ :>filename.txt
将从指定文件中删除所有内容filename.txt
,或者将其创建为空文件(如果不存在)。
这和rm有区别吗?
这与 rm 不同,因为 rm 会使文件从 中消失ls
,而不是使文件包含 0 字节。
与其他类似的清零或删除文件的方法相比,它的运行速度更快还是更慢?
同样,该文件不会被删除(从 给出的列表中消失ls
),它会转换为空文件(包含 0 字节)。
它应该比调用外部命令清空文件更快。必须创建一个子 shell 来加载可执行文件和exec
命令,使得外部命令比内置命令慢:
。
类似的解决方案有(有些设置$?
为1):
[ ] > filename.txt
builtin > filename.txt
command > filename.txt
printf '' > filename.txt
答案4
回复:性能:这在 shell 中尽可能高效;仅要求内核截断现有文件应该比取消链接文件并重新创建具有相同名称的新 inode 更有效。除非你想文件已删除,在这种情况下rm
还是unlink
它。
:
是一个内置的 shell,因此它避免了 fork/exec。true
普通现代 shell 中的等效项也是如此。
>foo
或者true > foo
让 shell 通过进行
open(path, O_WRONLY|O_TRUNC|O_CREAT, 0666)
系统调用来截断文件。
或者在 Linux 上使用 DASH 进行实践,从strace sh
输出来看:
openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
这是等效的。
然后又得去close()
那个FD了。事实上,当您使用 时,DASH 并没有特殊情况:>
,它会跳过以下环节:
openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10 # save original stdout
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1 # redirect stdout to foo
close(3) = 0 # then close 3
dup2(10, 1) = 1 # then restore original stdout
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
在 DASH 中使用> foo
会导致相同的系统调用序列,实际上重定向 fd1,然后恢复它。我没有检查bash
或其他外壳。
但这仍然比创建一个新流程来运行要便宜得多truncate -s 0 foo
(希望)制造一个单一的流程要便宜得多truncate("foo", 0)
系统调用这可能比open
+更有效close
。
在 C 语言(或任何具有系统调用绑定的语言)中,通过直接系统调用可以最有效地截断不想打开的文件truncate
。
在 Dash 中,3>foo
会导致以下系统调用序列:
openat(AT_FDCWD, "foo", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
close(3) = 0
打开一个新的 fd 会导致它已经是 fd 3,从而避免任何重复。这是 dash 中最有效的方法,可能比 . 节省几微秒>foo
。如果这很重要,那么 shell 脚本语言不适合您的任务!但你确实问了。
启用 Spectre+Meltdown 缓解后,-ENOSYS
在现代 Intel x86-64 上,即使是错误的系统调用也至少需要数千个时钟周期(即微秒)。只需一条syscall
指令即可实现用户->内核->用户往返的数百个。当然,路径查找等需要花费大量时间,文件系统代码也是如此。进入内核模式并返回通常会驱逐一些缓存,从而使用户空间在返回时运行速度变慢。
元数据写回磁盘后的实际 I/O 成本取决于文件系统。像 XFS 或现代 ext4 这样的 FS 在整个文件中仅使用 1 个或几个大范围,可以轻松地在 O(1) 时间内释放大量空间。或 O(n),其中n
是范围数(碎片)而不是字节大小。
根据 FS,如果盘区信息直接存储在 inode 中,而不是间接块中,那么就少了一件需要添加到空闲列表中的事情。
I/O 成本与取消链接文件类似,但您不必释放 inode 或修改目录条目。您仍然需要在截断时更新 inode 中的 mtime 和 ctime,但如果大小发生变化,您无论如何都必须写入它。