Lendo trechos de um pipe e execute o comando para cada pedaço

3

Eu tenho um comando (A) que gera um fluxo de dados para o STDOUT. O fluxo de dados é pequeno e, na maioria das vezes, nada é enviado. Eu quero canalizar esses dados para outro comando (B) que imediatamente executa um comando (C) para cada parte dos dados lidos (e canaliza os dados no trecho de leitura) quando a leitura pára de bloquear.

O tamanho dos pedaços é irrelevante e pode ser o que fizer sentido. Essencialmente, eu quero xargs, mas sem um delimitador, em vez disso, ele deve executar um comando sempre que a leitura começar a ser bloqueada.

Especificamente, o comando A para mim é "tail -f logfile" e o comando C é "hexdump -C". A razão pela qual não posso canalizar diretamente para "hexdump -C" é que o hexdump aguarda até ter lido 16 bytes completos antes de imprimir uma nova linha. Então, quero executar o comando para cada leitura de bloco.

    
por Hannes Landeholm 07.10.2015 / 18:27

2 respostas

3

Raw read não será bloqueado se o pipe não estiver vazio - ele retornará, no entanto, muitos bytes estarão disponíveis no pipe, mesmo que você peça mais. Você pode fazer uso disso e executar hexdump -C em cada retorno bem-sucedido de 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;
}

Você pode salvar isso, por exemplo, como shovel.c e, em seguida, make shovel (ou gcc shovel.c -o shovel ) e usá-lo no seu canal.

Ou, se você não estiver interessado nisso, aqui está um snippet de ruby que deve fazer a mesma coisa:

tail -f file |
ruby -e 'PSIZE=8*512; 
      while(bytes = STDIN.readpartial(PSIZE));
          IO.popen("hexdump -C","w") {|p| p.syswrite(bytes) }
      end '
    
por 07.10.2015 / 19:27
3

Se você quer algo brega que apenas imprime hex de seus dados como aparece você pode usar este script perl:

perl -e '$| = 1; $i = 1;
while(1){
 sysread(STDIN,$ch,1) or exit;
 printf "%02x%s",ord($ch),$i++%16==0?"\n":" ";
}'

E se você realmente quiser ler até um bloco e executar o 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);
 }
}'
    
por 07.10.2015 / 18:58

Tags