我有这样的场景:在大文件的开头和结尾添加行。
我已经尝试过,如下所示。
对于第一行:
sed -i '1i\'"$FirstLine" $Filename
对于最后一行:
sed -i '$ a\'"$Lastline" $Filename
但此命令的问题在于它附加文件的第一行并遍历整个文件。对于最后一行,它再次遍历整个文件并附加最后一行。由于文件非常大(14GB),这需要很长时间。
如何在仅读取文件一次的情况下在文件的开头添加一行,在文件的末尾添加另一行?
答案1
sed -i
使用临时文件作为实现细节,这就是您正在经历的;但是,将数据添加到数据流的开头而不覆盖现有内容需要重写文件,即使避免sed -i
.
如果重写文件不可行,您可能会考虑在读取文件时对其进行操作,例如:
{ echo some prepended text ; cat file ; } | command
另外,sed 用于编辑流——文件不是流。使用专门用于此目的的程序,例如 ed 或 ex。 sed选项-i
不仅不可移植,还会破坏文件的任何符号链接,因为它实际上会删除文件并重新创建它,这是毫无意义的。
您可以使用单个命令来完成此操作,如下ed
所示:
ed -s file << 'EOF'
0a
prepend these lines
to the beginning
.
$a
append these lines
to the end
.
w
EOF
请注意,根据您的 ed 实现,它可能会使用分页文件,要求您至少有那么多的可用空间。
答案2
请注意,如果您想避免在磁盘上分配文件的整个副本,您可以这样做:
sed '
1i\
begin
$a\
end' < file 1<> file
这利用了这样一个事实:当它的 stdin/stdout 是一个文件时,sed
按块读取和写入。因此,在这里,只要您添加的第一行小于sed
的块大小(应该类似于 4k 或 8k),它就可以覆盖它正在读取的文件。
请注意,如果由于某种原因sed
失败(被杀死、机器崩溃......),您最终将得到一半的文件处理,这意味着第一行大小的一些数据在中间的某个地方丢失。
另请注意,除非您sed
是 GNU sed
,否则不适用于二进制数据(但由于您使用的是-i
,所以您正在使用 GNU sed )。
答案3
以下是一些选择(所有这些都会创建文件的新副本,因此请确保有足够的空间):
简单的回声/猫
echo "first" > new_file; cat $File >> new_file; \ echo "last" >> new_file;
awk/gawk 等
gawk 'BEGIN{print "first\n"}{print}END{print "last\n"}' $File > NewFile
awk
及其同类逐行读取文件。该BEGIN{}
块在第一行之前执行,END{}
在最后一行之后执行。所以,上面的命令的意思是print "first" at the beginning, then print every line in the file and print "last" at the end
。珀尔
perl -ne 'BEGIN{print "first\n"} print;END{print "last\n"}' $File > NewFile
这与上面用 Perl 编写的 gawk 本质上是一样的。
答案4
您可以在 Ex 模式下使用 Vim:
ex -sc '1i|ALFA' -c '$a|BRAVO' -cx file
1
选择第一行i
插入文本和换行符$
选择最后一行a
追加文本和换行符x
保存并关闭