альтернатива popen()

Мой вопрос является продолжением этого: popen создает дополнительный процесс sh

Мотивы:

1) Моя программа должна создать дочерний элемент, который выполняет tail в файле. Мне нужно обработать вывод построчно. Вот почему я использую popen, потому что он возвращает ФАЙЛ *. Я могу легко получить одну строку, сделать то, что мне нужно, и распечатать ее.

Одна проблема с popen заключается в том, что вы не получаете pid дочернего элемента (команда tail в моем случае).

2) Моя программа не должна завершаться до завершения ее дочернего элемента. Так что мне нужно сделать wait; но без pid я не могу этого сделать.

Как я могу достичь обеих целей?

Возможное (запутанное) решение: выполните execvp("tail -f file > tmpfile") и продолжайте читать этот tmpfile. Однако я не уверен, насколько хорошо это решение.


person hari    schedule 19.07.2011    source источник


Ответы (4)


Почему вы не используете метод pipe/fork/exec?

pid_t pid = 0;
int pipefd[2];
FILE* output;
char line[256];
int status;

pipe(pipefd); //create a pipe
pid = fork(); //span a child process
if (pid == 0)
{
// Child. Let's redirect its standard output to our pipe and replace process with tail
 close(pipefd[0]);
 dup2(pipefd[1], STDOUT_FILENO);
 dup2(pipefd[1], STDERR_FILENO);
 execl("/usr/bin/tail", "/usr/bin/tail", "-f", "path/to/your/file", (char*) NULL);
}

//Only parent gets here. Listen to what the tail says
close(pipefd[1]);
output = fdopen(pipefd[0], "r");

while(fgets(line, sizeof(line), output)) //listen to what tail writes to its standard output
{
//if you need to kill the tail application, just kill it:
  if(something_goes_wrong)
    kill(pid, SIGKILL);
}

//or wait for the child process to terminate
waitpid(pid, &status, 0);
person Patryk    schedule 19.07.2011
comment
Нужно ли мне close(pipefd[0]) после того, как я закончу читать в конце? или прямо перед kill? - person hari; 03.08.2011
comment
Между прочим, если вы используете GLib, есть готовая реализация с множеством функций, плюс она обрабатывает все коды ошибок и так далее. См. developer.gnome.org/glib/2.30/glib-Spawning-Processes. .html (отказ от ответственности: я реализовал это, хотя и много лет назад) - person Havoc P; 12.12.2011
comment
@havoc или семейство функций libHX HXproc_*, в котором не используются функции с таким неудобным количеством параметров, как glib. - person jørgensen; 12.12.2011
comment
конечно, просто упоминание библиотеки, которую я знаю. - person Havoc P; 12.12.2011

  1. Вы можете использовать pipe, функцию семейства exec* и fdopen. Это нестандартно, но popen тоже.
  2. Вам не нужно wait. Просто прочитайте трубку до EOF.
  3. execvp("tail -f file > tmpfile") не будет работать, перенаправление — это функция оболочки, и здесь вы не запускаете оболочку. Даже если бы это сработало, это было бы ужасным решением. Предположим, вы прочитали файл до конца, но дочерний процесс еще не завершился. Что вы делаете?
person n. 1.8e9-where's-my-share m.    schedule 19.07.2011
comment
1. Они не являются нестандартными, просто выходят за рамки стандарта C. Они являются частью стандарта POSIX. - person kennytm; 19.07.2011

Вы можете использовать wait, так как он не хочет, чтобы PID ждал, а просто ожидает завершения любого дочернего процесса. Если вы создали другие дочерние процессы, вы можете отслеживать их, и если wait возвращает неизвестный PID, вы можете предположить, что это из вашего popen процесса.

person DarkDust    schedule 19.07.2011
comment
Если в каком-то случае ошибки мне нужно убить ребенка, могу ли я это сделать? (возникает ли этот сценарий вообще?), я просто думаю вслух. - person hari; 19.07.2011
comment
Допустим, в программе произошла ошибка, и она должна выйти. wait Убедится ли, что дочерний процесс также будет убит/завершен перед выходом из основной программы? - person hari; 19.07.2011
comment
Вы можете создать группу процессов, то простой вызов kill(getpgrp(), SIGTERM); уничтожит все ваши процессы. - person DarkDust; 19.07.2011

Я не уверен, зачем вам нужен идентификатор процесса ребенка. Когда дочерний процесс завершится, чтение канала вернет EOF. Если вам нужно завершить дочерний процесс, просто закройте канал.

person jbruni    schedule 19.07.2011
comment
дочерний элемент может быть tail -f, который никогда не возвращается. В этом случае, когда я нажимаю ctrl-C, перед выходом из моей основной программы я хочу, чтобы дочерний элемент - tail -f был выполнен. - person hari; 19.07.2011