代码语言:javascript复制这篇文章是来自我在 0xffff.one 上的一个帖子 https://0xffff.one/d/900 的回复。 原帖内容: 在折腾一个超大的备份文件,需要把它的前 41 个字节删除掉,没有 WinHEX,想着用 dd 命令来实现 一开始这么干,发现速度奇慢,5分钟过去才复制40MB…
dd if=input.bak bs=1 skip=41 > result.bak
代码语言:javascript复制google 一波发现一老哥的操作,配合 dd 和 cat 实现快速拷贝的功能,有些佩服。
{ dd bs=41 skip=1 count=0; cat; } < input.bak > result.bak
原帖: https://unix.stackexchange.com/questions/6852/best-way-to-remove-bytes-from-the-start-of-a-file
干了啥
看一下一开始的指令:
代码语言:javascript复制dd if=input.bak bs=1 skip=41 > result.bak
为什么这个指令会慢呢?首先一点背景知识:
计算机中每一次向硬盘读取和写入数据,无论读多小的数据量,都至少需要花一段常数时间(称为overhead)。 (就像你去超市买鸡蛋一样,无论你一次只买一个,还是一千个,你都至少要花从家走到超市,再从超市走回家的时间。)
就好比你现在要100个鸡蛋,但是你去超市一趟只买一颗鸡蛋的话,你就要来回跑100次一样。用 dd 拷文件也是同样的道理,如果一次只跑去给硬盘要一个字节,一个文件就要来回跑特别多次,花费的时间就会特别长。
为了解决这个问题,dd 在读文件的时候,会将文件切分成大小固定的一小块一小块 (block),每次向硬盘要数据就一次性要一个“块”的大小(默认 512 个字节),也就是说,每次费那么大功夫跑过去,那就干脆多要一点数据,同样大小的文件不就可以少跑很多次了吗?
所以为啥慢?
前面提到的一次拿一个分块,这个分块的具体大小,是可以通过bs=
参数进行人工调节的(bs = block size, 块大小)。
我们一开始的指令的问题就在于,我们这个指令里有一个参数bs=1
,也就是告诉 dd,我想要每 1byte 就当作一个 block,这样 dd 实际上就又变回了跑一次读一个字节的傻 dd 了,所以这个指令最后就慢得出奇啦。(0.26MB/s)
大不就完事了?
路人甲:那么既然是因为块大小 bs 设太小了,那我们改大不就行了吗?
没错,理论上是这样的。划重点:理论上
如果我们只是想要单纯的把文件a.txt
拷贝一份到文件b.txt
,那我们的确可以直接把 bs 改大就行了:
# 块大小:512Bytes,速度93MB/s
dd if=a.txt of=b.txt bs=512
# 块大小:4MB,速度1138MB/s
dd if=a.txt of=b.txt bs=4m
仅仅把每次读的数据块大小改大,就得到 12 倍的速度提升!
然鹅,为什么我们这个例子里不行呢?
代码语言:javascript复制dd if=input.bak bs=1 skip=41 > result.bak # 为啥不把 bs 直接改大???
因为我们除了拷贝文件外,还有另外一个要求:跳过前41个字节!
那么我们用什么方法实现跳过41个字节呢?我们一开始的指令使用了 skip=41
来实现这一要求。
好,刹住,大问题来了:skip 的单位是 block!也就是说skip=41
不是指跳过41个字节,是指跳过41个 block