我有一个命令 (A),它将数据流输出到 STDOUT。数据流很小,大多数时候什么也不发送。我想将该数据通过管道传输到另一个命令 (B),当读取停止阻塞时,该命令立即为读取的每个数据块执行命令 (C)(并通过管道传入读取块中的数据)。
块的大小无关紧要,可以是任何有意义的大小。本质上我想要 xargs 但没有分隔符,相反,它应该在读取开始阻塞时执行命令。
具体来说,命令 A 对我来说是“tail -f logfile”,命令 C 是“hexdump -C”。我无法直接通过管道输入“hexdump -C”的原因是 hexdump 会等待读取完整的 16 个字节,然后再打印新行。所以我想为每个读取的块执行命令。
答案1
read
如果管道不为空,Raw不会阻塞——无论管道中有多少字节可用,即使您要求更多字节,它都会返回。您可以利用它并hexdump -C
在每次成功返回时运行read
。
#define _BSD_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define N (8*512) //pipe size on my system as shown in `ulimit -a`
static char buf[N];
int main(){
ssize_t nread;
FILE* p;
for(;;){
do { nread = read(0, buf, N); }while (nread < 0 && errno == EINTR);
if(nread == 0) return 0; //EOF
if(nread < 0) goto error;
p = popen("hexdump -C", "w"); if(!p) goto error;
if(fwrite(buf, sizeof(char), nread, p) != nread) goto error;
pclose(p);
}
return 0;
error:
perror(""); return 1;
}
您可以将其保存为,例如,shovel.c
然后make shovel
(或gcc shovel.c -o shovel
)并在管道中使用它。
或者,如果您对此不感兴趣,这里有一个 Ruby 代码片段,应该可以执行相同的操作:
tail -f file |
ruby -e 'PSIZE=8*512;
while(bytes = STDIN.readpartial(PSIZE));
IO.popen("hexdump -C","w") {|p| p.syswrite(bytes) }
end '
答案2
如果你想要一些简单的东西,只打印你的数据的十六进制,你可以使用这个 perl 脚本:
perl -e '$| = 1; $i = 1;
while(1){
sysread(STDIN,$ch,1) or exit;
printf "%02x%s",ord($ch),$i++%16==0?"\n":" ";
}'
如果您确实想读取直到一个块并运行 hexdump:
perl -e '$| = 1; $i = 1; $rin = ""; vec($rin, fileno(STDIN), 1) = 1;
open(F,"|hexdump -C") or die;
while(1){
$nfound = select($rout=$rin, undef, undef, 0);
if($nfound==1){
sysread(STDIN,$data,999) or exit;
syswrite(F,$data) or die;
}else{
close(F);
open(F,"|hexdump -C") or die;
select($rout=$rin, undef, undef, undef);
}
}'