Я пытаюсь объединить несколько программ примерно так (пожалуйста, игнорируйте любые дополнительные включения, это тяжелая работа в процессе):
pv -q -l -L 1 < input.csv | ./repeat <(nc "host" 1234)
Где источник программы повтора выглядит следующим образом:
#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;
}
Я заметил это:
- Когда я просто использую трубу
./repeat
, все работает как задумано. - Когда я просто использую процесс подстановки, все работает как задумано.
- Когда я инкапсулирую pv, используя подстановку процессов, все работает как задумано.
- Однако, когда я использую определенную конструкцию, я теряю данные (отдельные символы) из stdin!
Я пробовал следующее:
- Я попытался отключить буферизацию в канале между всеми процессами
pv
и./repeat
использовать ихstdbuf -i0 -o0 -e0
, но это, похоже, не работает. - Я обменял epoll на опрос, не работает.
- Когда я смотрю на поток между
pv
и./repeat
сtee stream.csv
, это выглядит правильно. - Раньше я
strace
видел, что происходит, и я вижу много однобайтовых операций чтения (как и ожидалось), и они также показывают, что данные пропадают.
Интересно, что происходит? Или что я могу сделать, чтобы продолжить расследование?
<(...)
? Есть ли лучший способ, чем<( 0<&- ...)
?<(... </dev/null)
, не используйте0<&-
: это приведет к тому, что первыйopen(2)
вернется0
как новый fd. Если вашnc
поддерживает его, вы также можете использовать эту-d
опцию.Функция epoll () или poll (), возвращаемая с помощью E / POLLIN, сообщит вам только о том, что одно чтение () может не блокироваться.
Не то чтобы вы могли делать много однобайтовых операций чтения () до новой строки, как и вы.
Я говорю, может, потому что read () после epoll (), возвращенного с E / POLLIN, все еще может блокироваться.
Ваш код также будет пытаться прочитать последние EOF и полностью игнорирует любые ошибки read ().
источник
repeat
программа по сути обрабатывает данные NMEA (линейные и без указателей длины) из нескольких источников. Поскольку я объединяю данные из нескольких живых источников, я бы хотел, чтобы мое решение было небуферизованным. Можете ли вы предложить более эффективный способ сделать это?ypee
утилиты, которая читает из нескольких файлов и смешивает их в другой файл, сохраняя записи (сохраняя строки нетронутыми).{ cmd1 & cmd2 & cmd3; } > file
файл будет содержать то, что вы описываете. Однако в моем случае я запускаю все из tcpserver (3), поэтому я хочу также включить stdin (который содержит данные клиента). Я не уверен, как это сделать.