После некоторых экспериментов я считаю, что проблема в том, что bash
ожидает завершения всех процессов в конвейере, в той или иной форме.
С простым файлом 'qqq'примерно 360 строк исходного кода C (различные программы объединены несколько раз), и, используя 'grep -q return', я наблюдаю:
tail -n 300 qqq | grep -q return
завершается почти сразу. tail -n 300 -f qqq | grep -q return
не выходит. tail -n 300 -f qqq | strace -o grep.strace -q return
не выходит, пока не прервано.Файл grep.strace
заканчивается на:
read(0, "#else\n#define _XOPEN_SOURCE 500\n"..., 32768) = 10152
close(1) = 0
exit_group(0) = ?
Это заставляет меня думать, что grep
завершился до того, как прерывание убивает tail
;если он чего-то ждал, это означало бы, что он получил сигнал.
Простая программа, которая имитирует то, что делает оболочка, но без ожидания, указывает, что все заканчивается.
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
static void err_error(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(void)
{
int p[2];
if (pipe(p) != 0)
err_error("Failed to create pipe\n");
pid_t pid;
if ((pid = fork()) < 0)
err_error("Failed to fork\n");
else if (pid == 0)
{
char *tail[] = { "tail", "-f", "-n", "300", "qqq", 0 };
dup2(p[1], 1);
close(p[0]);
close(p[1]);
execvp(tail[0], tail);
err_error("Failed to exec tail command");
}
else
{
char *grep[] = { "grep", "-q", "return", 0 };
dup2(p[0], 0);
close(p[0]);
close(p[1]);
execvp(grep[0], grep);
err_error("Failed to exec grep command");
}
err_error("This can't happen!\n");
return -1;
}
С файлом фиксированного размера, tail -f
не собирается выходить - поэтому оболочка (bash
), кажется, зависает.
tail -n 300 -f qqq | grep -q return
торчал, но когда я использовал другой терминал, чтобы добавить еще 300 строк в файл qqq
, команда вышла.Я интерпретирую это как происходящее, потому что grep
вышел, поэтому, когда tail
записал новые данные в канал, он получил SIGPIPE и завершился, и поэтому bash
признал, что все процессы в конвейере были мертвы.
Я наблюдал одинаковое поведение как с ksh
, так и с bash
.Это говорит о том, что это не ошибка, а ожидаемое поведение.Тестирование в Linux (RHEL 5) на компьютере x86_64.