从管道读取块并为每个块执行命令

从管道读取块并为每个块执行命令

我有一个命令 (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);
 }
}'

相关内容