Estou tentando combinar alguns programas como esse (ignore todas as inclusões extras, esse é um trabalho pesado em andamento):
pv -q -l -L 1 < input.csv | ./repeat <(nc "host" 1234)
Onde a fonte do programa de repetição é a seguinte:
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
#include <string>
inline std::string readline(int fd, const size_t len, const char delim = '\n')
{
std::string result;
char c = 0;
for(size_t i=0; i < len; i++)
{
const int read_result = read(fd, &c, sizeof(c));
if(read_result != sizeof(c))
break;
else
{
result += c;
if(c == delim)
break;
}
}
return result;
}
int main(int argc, char ** argv)
{
constexpr int max_events = 10;
const int fd_stdin = fileno(stdin);
if (fd_stdin < 0)
{
std::cerr << "#Failed to setup standard input" << std::endl;
return -1;
}
/* General poll setup */
int epoll_fd = epoll_create1(0);
if(epoll_fd == -1) perror("epoll_create1: ");
{
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd_stdin;
const int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_stdin, &event);
if(result == -1) std::cerr << "epoll_ctl add for fd " << fd_stdin << " failed: " << strerror(errno) << std::endl;
}
if (argc > 1)
{
for (int i = 1; i < argc; i++)
{
const char * filename = argv[i];
const int fd = open(filename, O_RDONLY);
if (fd < 0)
std::cerr << "#Error opening file " << filename << ": error #" << errno << ": " << strerror(errno) << std::endl;
else
{
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = fd;
const int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event);
if(result == -1) std::cerr << "epoll_ctl add for fd " << fd << "(" << filename << ") failed: " << strerror(errno) << std::endl;
else std::cerr << "Added fd " << fd << " (" << filename << ") to epoll!" << std::endl;
}
}
}
struct epoll_event events[max_events];
while(int event_count = epoll_wait(epoll_fd, events, max_events, -1))
{
for (int i = 0; i < event_count; i++)
{
const std::string line = readline(events[i].data.fd, 512);
if(line.length() > 0)
std::cout << line << std::endl;
}
}
return 0;
}
Eu notei isso:
- Quando apenas uso o cachimbo
./repeat
, tudo funciona como pretendido. - Quando apenas uso a substituição do processo, tudo funciona como pretendido.
- Quando encapsulo o pv usando substituição de processo, tudo funciona como pretendido.
- No entanto, quando uso a construção específica, pareço perder dados (caracteres individuais) do stdin!
Eu tentei o seguinte:
- Eu tentei desativar o buffer no canal entre
pv
e./repeat
usandostdbuf -i0 -o0 -e0
em todos os processos, mas isso não parece funcionar. - Troquei epoll por pesquisa, não funciona.
- Quando olho para o fluxo entre
pv
e./repeat
comtee stream.csv
, isso parece correto. - Eu costumava
strace
ver o que estava acontecendo, e vejo muitas leituras de byte único (conforme o esperado) e elas também mostram que os dados estão faltando.
Me pergunto o que está acontencedo? Ou o que posso fazer para investigar mais?
<(...)
? Existe uma maneira melhor do que<( 0<&- ...)
?<(... </dev/null)
. não use0<&-
: fará com que o primeiroopen(2)
retorne0
como o novo fd. Se o seunc
suporte, você também pode usar a-d
opçãoepoll () ou poll () retornando com E / POLLIN apenas informará que uma única leitura () pode não ser bloqueada.
Não que você consiga executar muitos bytes de leitura () até uma nova linha, como você faz.
Digo maio porque um read () após epoll () retornado com E / POLLIN ainda pode bloquear.
Seu código também tentará ler o EOF passado e ignora completamente todos os erros de leitura ().
fonte
repeat
programa está processando essencialmente dados NMEA (baseados em linhas e sem indicadores de comprimento) de várias fontes. Como estou combinando dados de várias fontes ativas, gostaria que minha solução fosse sem buffer. Você pode sugerir uma maneira mais eficiente de fazer isso?ypee
utilitário que lê vários fds e os mistura em outro fd, preservando registros (mantendo as linhas intactas).{ cmd1 & cmd2 & cmd3; } > file
arquivo conterá o que você descreve. No entanto, no meu caso, estou executando tudo a partir do tcpserver (3), então quero incluir o stdin (que contém os dados do cliente) também. Não sei bem como fazer isso.