我有一个 1 TB 的文件。我想从字节 12345678901 读取到字节 19876543212 并将其放在具有 100 MB RAM 的机器上的标准输出上。
我可以轻松编写一个执行此操作的 perl 脚本。sysread 提供 700 MB/s(很好),但 syswrite 仅提供 30 MB/s。我想要更高效的东西,最好是每个 Unix 系统都安装的东西,并且可以以 1 GB/s 的速度交付。
我的第一个想法是:
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
Run Code Online (Sandbox Code Playgroud)
但这效率不高。
编辑:
我不知道我是如何测量 syswrite 错误的。这提供了 3.5 GB/s:
perl -e 'sysseek(STDIN,shift,0) || die; $left = shift; \
while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){ \
$left -= $read; syswrite(STDOUT,$buf);
}' 12345678901 $((19876543212-12345678901)) < bigfile
Run Code Online (Sandbox Code Playgroud)
并避免yes | dd bs=1024k count=10 | wc
噩梦。
Gra*_*eme 22
由于块大小小,这很慢。使用最新的 GNU dd
( coreutils v8.16 + ),最简单的方法是使用skip_bytes
和count_bytes
选项:
in_file=1tb
start=12345678901
end=19876543212
block_size=4096
copy_size=$(( $end - $start ))
dd if="$in_file" iflag=skip_bytes,count_bytes,fullblock bs="$block_size" \
skip="$start" count="$copy_size"
Run Code Online (Sandbox Code Playgroud)
fullblock
根据@Gilles 的回答在上面添加了选项。起初我认为它可能是由 暗示的count_bytes
,但事实并非如此。
提到的问题是下面的一个潜在问题,如果dd
读/写调用因任何原因中断,那么数据将丢失。这在大多数情况下是不可能的(因为我们从文件而不是管道中读取,所以几率会有所降低)。
使用dd
没有skip_bytes
和count_bytes
选项的 a 更困难:
in_file=1tb
start=12345678901
end=19876543212
block_size=4096
copy_full_size=$(( $end - $start ))
copy1_size=$(( $block_size - ($start % $block_size) ))
copy2_start=$(( $start + $copy1_size ))
copy2_skip=$(( $copy2_start / $block_size ))
copy2_blocks=$(( ($end - $copy2_start) / $block_size ))
copy3_start=$(( ($copy2_skip + $copy2_blocks) * $block_size ))
copy3_size=$(( $end - $copy3_start ))
{
dd if="$in_file" bs=1 skip="$start" count="$copy1_size"
dd if="$in_file" bs="$block_size" skip="$copy2_skip" count="$copy2_blocks"
dd if="$in_file" bs=1 skip="$copy3_start" count="$copy3_size"
}
Run Code Online (Sandbox Code Playgroud)
您也可以尝试不同的块大小,但收益不会很大。请参阅 -有没有办法确定 dd 的 bs 参数的最佳值?
bs=1
告诉一次dd
读取和写入一个字节。每个read
和write
调用都有开销,这使得它变慢。使用更大的块大小以获得不错的性能。
当您复制整个文件,在Linux下至少,我发现,cp
和cat
比快dd
,即使你指定一个较大的块大小。
要仅复制文件的一部分,您可以通过管道tail
导入head
. 这需要 GNU coreutils 或其他一些head -c
必须复制指定字节数的实现(tail -c
在 POSIX 中但head -c
不是)。Linux 上的快速基准测试表明这比 慢dd
,大概是因为管道。
tail -c $((2345678901+1)) | head -c $((19876543212-2345678901))
Run Code Online (Sandbox Code Playgroud)
问题dd
在于它不可靠:它可以复制部分数据。据我所知,dd
读取和写入常规文件时是安全的 - 请参阅dd 何时适合复制数据?(或者,什么时候 read() 和 write() 是部分的) ——但前提是它没有被信号中断。使用 GNU coreutils,您可以使用该fullblock
标志,但这不可移植。
另一个问题dd
是很难找到有效的块计数,因为跳过的字节数和传输的字节数都需要是块大小的倍数。您可以使用多次调用dd
:一个复制第一个部分块,一个复制大部分对齐的块,一个复制最后一个部分块 - 请参阅Graeme's answer for a shell snippet。但是不要忘记你在运行脚本时,除非你正在使用fullblock
标志,否则你需要祈祷它dd
会复制所有数据。dd
如果副本是部分副本,则返回非零状态,因此很容易检测到错误,但没有实用的方法来修复它。
POSIX 在 shell 级别没有什么比这更好的了。我的建议是编写一个小的专用 C 程序(具体取决于您实现的内容,您可以将其称为dd_done_right
或tail_head
或mini-busybox
)。
归档时间: |
|
查看次数: |
14273 次 |
最近记录: |