我有一个脚本想要在一个非常大的文件上运行几个程序/管道。示例:
26-06-1993
内核将尝试将文件缓存在RAM中,因此如果很快再次读取它,则可能是RAM中的副本。但是,文件很大,程序运行速度也大相径庭,因此不太可能有效。为了最大程度地减少IO使用率,我想一次读取文件。
我知道使用tee和moreutils的小便复制数据的两种方法:
grep "ABC" file > file.filt
md5sum file > file.md5
还有另一种“最佳”方法吗?哪种方法制作的副本最少? >()或|-编辑到哪个程序是否有所不同?如果一个程序太慢,这些方法是否会尝试在RAM中缓冲数据?它们如何扩展到许多阅读器程序?
答案 0 :(得分:1)
tee
(命令)使用fopen
打开每个文件,但在每个文件上设置_IONBF
(无缓冲)。它从标准输入中read
到每个文件* fwrite
。
pee
(命令),每个命令popen
,将每个命令设置为无缓冲,从标准输入中设置read
,并且将每个文件*设置fwrite
。
popen
使用pipe(2),其容量为65536字节。写入完整缓冲区将被阻止。 pee
也使用/ bin / sh来解释命令,但我认为不会添加任何缓冲/复制。
mkfifo
(命令)使用mkfifo
(libc),后者在下面使用管道,打开文件/管道块,直到另一端打开。
bash
<>()语法(sub.c:5712)使用pipe
或mkfifo
。 pipe
(如果支持/ dev / fds)。它不使用c fopen
个调用,因此不设置缓冲。
因此,所有三个变体(小便,三通>(),mkfifo ...)都应具有相同的行为,从stdin读取并在不进行缓冲的情况下写入管道。数据在每次读取(从内核到用户)时都重复,然后在每次写入(从用户回到内核)时都重复,我认为 tee
的fwrites不会引起额外的复制(因为没有缓冲区)。内存使用量最多可以增加到65536 * num_readers + 1 * read_size(如果没有人正在读取)。 tee
首先写入stdout,然后依次写入每个文件/管道。
鉴于此小便只能在其他shell(鱼!)上工作,而缺少>()运算符等效项,因此bash似乎不需要它。如果您不喜欢bash,我更喜欢使用tee,但是如果您不喜欢bee,则它很适合。 bash <()当然不会被小便代替。手动进行mkfifoing和重定向很棘手,不太可能很好地处理错误。
pee
可以通过使用tee
库函数(而不是fwrite)来实现。我认为这将导致以最快的读取器的速度读取输入,并可能填满内核缓冲区。
答案 1 :(得分:0)
AFAIK,没有“最佳方法”来实现这一目标。但是我可以给您另一种方法,更详细些,不是一条线,而是更清晰些,因为每个命令都是自己编写的。使用命名管道:
mkfifo tmp1 tmp2
tee tmp1 > tmp2 < file &
cat tmp1 | md5sum > file.md5 &
cat tmp2 | grep "ABC" > file.filt &
wait
rm tmp1 tmp2
tee
将输入文件输入命名管道(tee在标准输出中输出其输入,因此姓氏管道必须是重定向),使其在后台运行。这种方法的缺点是,当程序的速度有所变化时,所有程序都将以相同的速度读取文件(限制是缓冲区大小,一旦其中一个管道已满,其他管道将具有也要等待),因此,如果其中之一需要大量资源(如内存不足),则资源将用于所有进程的整个生命周期。