是否可以在开头截断文件(就地,相同的索引节点)?

是否可以在开头截断文件(就地,相同的索引节点)?

可以删除 a 的尾随字节,file而无需写入新文件 ( > newfile) 并将其移回 ( mv newfile file)。这是通过以下方式完成的truncate

truncate -s -1 file

可以删除前导字节,但通过移动它(这会改变 inode)(对于某些版本的 tail):

tail -c +1 file > newfile ; mv newfile file

那么:如何在不移动文件的情况下做到这一点?
理想情况下,就像截断一样,即使对于非常大的文件也只需要更改几个字节。

注意:sed -i将更改文件索引节点,因此,即使它可能有用,也不是这个问题的答案(IMO)。

答案1

ksh93

tail -c+2 < file 1<>; file

(其中<>;是标准<>运算符的 ksh93 特定变体,如果重定向的命令成功,它最终会截断文件)。

将删除第一个字节(通过将文件的其余部分写入自身并在末尾截断)。

同样可以用以下方法完成sh

{
  tail -c+2 < file &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file

请注意,它会不稀疏稀疏文件(尽管之后您仍然可以重新挖洞fallocate -d)。

出现读/写错误时,tail可能会退出,使文件部分被覆盖(因此,例如,abcdefgh可能最终会导致bcddefgh重写后失败bcd)。您可以调整上述内容,以便它在出现错误时报告写入偏移量,以便您知道如何恢复数据。仍然有ksh93

unset -v offset
{ tail -c+2 < file || false >#((offset=CUR)); } 1<>; file

之后设置 if $offset,它包含已成功写入的数据量。

在 Linux(自 3.15 起)和 ext4 或 xfs 文件系统上,可以坍塌fallocate()大小和偏移量的范围或字节,是系统调用或实用程序的文件系统块大小的倍数fallocate

例如

fallocate -c -l 8192 file

将删除文件的前 8192 字节(假设 FS 的块大小是 8192 的除数),而不必重写文件的其余部分。但如果您想删除不是 FS 块大小倍数的部分,那么这是没有用的。

答案2

取决于您所说的“非常大的文件”是什么意思。你的极限是什么?

您可以将整个内容读入内存(作为 awk 字符串),并将子字符串写回原始文件。在某个阶段,awk 会同时包含原始数据和子字符串,但对于 0.5 GB 来说这是一个可行的解决方案。 awk 在我的笔记本电脑上每秒可以处理大约 80 MB 的数据。

在 C 中很容易,因为您只需移动写入的起始指针即可。

答案3

这在 C 语言中相当简单,使用相同的索引节点,不使用工作文件。只需要小心才能做好。您可能需要初步查询来查找设备的块大小(例如 4096),尽管 2 的任意幂可能就足够了(例如 64K)。

将数据流可视化为一条毛毛虫:向前伸展,让数据进入新位置。

打开文件读/写,并在系统调用读/写中执行所有操作,以避免 FILE* 例程中可能出现的缓冲问题。

要从文件前面 (N) 删除的字节计数是完整块的数量和一些备用字节(这些组件中的一个或两个可以为零)。

寻找 A,读取 X * 4096 字节,其中 X 选择较大(为了效率)但不是愚蠢的大。也许 4MB 缓冲区是一个最佳选择。

寻找 0,将该缓冲区写入它应占用的完整块数。据我在纸上看到的,它永远不会自行包装——下一个未读字节不能位于较早的块中。

冲洗并重复(两次搜索时增加 4MB),直到文件用完。正确处理任何短块。

这会给您留下最后 N 个字节的额外副本,您可以通过系统调用将其截断。

性能应该不错。写入是块对齐的。理论上,由于重叠,每次读取需要两次块访问,但连续读取可以避免这种情况(例如,4MB 读取 1025 个块而不是 1024 个块)。

我认为这可以在带有 dd 命令的脚本中完成,但是 dd 中的块大小选项适用于查找和读取,因此效率非常低。

测试方法:获取一个100MB的随机数据文件,对其进行cksum。然后将其附加到一个较小的 N 字节文件中。运行代码、cksum,并证明该文件现在与您附加的内容相同。计时吧。使用各种 N 值进行测试,包括 0、< 1 块、确切数量的块、几个块 + 一个位以及整个文件。

我是否必须编写并测试代码才能获得赏金?

答案4

你可以用printfand来做到这一点sed,尽管这是非常危险的即时编辑文件而不使用临时副本我极力推荐您阅读这篇精彩的文章在做之前。

另外,请记住,如果文件太大并且您没有足够的内存,这可能会失败

printf '%s\n' "$(sed '1d' test.txt)" > test.txt

也就是说,这应该可以工作并动态修改文件而不更改索引节点。

相关内容