Используя dup2 и execv для получения содержимого папки в C

Я написал программу для использования команды ls в терминале Linux, чтобы прочитать содержимое папки и записать текст из ls на экран с моей программой на Си. Вот код, который я написал:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int pipe_ends[2];
    if(pipe(pipe_ends)) 
    {
        printf("Could not create pipe\n");
        return -1;
    }
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("fork");
        exit(1);
    }
    if(!pid)
    {
        dup2(pipe_ends[0],1);
        char* args[3];
        args[0] = "/bin/ls";
        args[1] = "-l";
        args[2] = NULL;
        execv("/bin/ls",args);
        printf("something went wrong\n");
    }

    char buff[10240];
    int count = read(pipe_ends[1],buff,10240);
    buff[count] = '\0';
    printf("here goes nothing......\n");
    printf("%s",buff);
    return 0;   
}

Вывод, который я получаю для этой программы:

here goes nothing......
od@od-Inspiron-N5110:~/Documents/work/new/CO/project1$ /bin/ls: write error: Bad file descriptor

od@od-Inspiron-N5110:~/Documents/work/new/CO/project1$

Кажется, что чтение было сделано до записи. Но я думал, что чтение блокирует. Пожалуйста, помогите мне найти ошибку здесь.

Заранее спасибо.

2 ответа

Решение

У вас было три проблемы:

  1. Из справочной страницы: массив pipefd используется для возврата двух файловых дескрипторов, относящихся к концам канала. pipefd[0] относится к концу чтения канала. pipefd[1] относится к концу записи канала. Вы использовали pipefd[0] написать и pipefd[1] читать. Это не сработает. Это было основной причиной EBADF (неверный дескриптор файла) ошибка. Если вы хотите двунаправленность, используйте socketpair()

  2. Когда ты fork(), вам нужно закрыть файловые дескрипторы, которые вам не нужны. Это другой конец трубы.

  3. Я считаю, что вы должны close() FD перед вами dup2 над ним, хотя страница руководства не является явной. В этом случае close() существующий STDOUT прежде чем использовать dup2(),

Вот исправленная версия, которая работает (минимальные изменения, внесенные для того, чтобы она работала, помечены комментариями, есть еще кое-что, что вы можете сделать, чтобы сделать ее идеальной):

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int
main ()
{
  int pipe_ends[2];
  if (pipe (pipe_ends))
    {
      printf ("Could not create pipe\n");
      return -1;
    }
  pid_t pid = fork ();
  if (pid < 0)
    {
      perror ("fork");
      exit (1);
    }
  if (!pid)
    {
      close (1); /* close previous stdout */
      /* dup write end of pipe */
      dup2 (pipe_ends[1], 1);
      /* close read end */
      close (pipe_ends[0]);
      char *args[3];
      args[0] = "/bin/ls";
      args[1] = "-l";
      args[2] = NULL;
      execv ("/bin/ls", args);
      printf ("something went wrong\n");
    }

  close (pipe_ends[1]); /* close write end of pipe */

  char buff[10240];
  int count = read (pipe_ends[0], buff, 10240);
  buff[count] = '\0';
  printf ("here goes nothing......\n");
  printf ("%s", buff);
  return 0;
}

И доказательство пудинга:

amb@nimrod-ubuntu:~/so$ ./p
here goes nothing......
total 32
-rwxrwxr-x 1 amb amb 8889 Jan 19 09:19 p
-rw-rw-r-- 1 amb amb  853 Jan 19 09:19 p.c
-rwxrwxr-x 1 amb amb 8456 Jan 18 20:47 test
-rw-rw-r-- 1 amb amb  243 Jan 18 20:47 test.c

Вы должны закрыть неиспользуемые концы ваших каналов - особенно конец записи канала в родительском процессе, так как read() в родительском не будет EOF, в то время как родительский конец канала записи еще открыт. Кроме того, вам нужно прочитать с конца чтения канала (pipe_ends[0]), и продублируйте конец записи канала для стандартного вывода ls,

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    int pipe_ends[2];
    if (pipe(pipe_ends))
    {
        printf("Could not create pipe\n");
        return -1;
    }
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(1);
    }
    if (pid == 0)
    {
        dup2(pipe_ends[1], 1);
        close(pipe_ends[0]);
        close(pipe_ends[1]);
        char *args[3];
        args[0] = "/bin/ls";
        args[1] = "-l";
        args[2] = NULL;
        execv("/bin/ls", args);
        printf("something went wrong\n");
        return 1;
    }

    close(pipe_ends[1]);
    char buff[10240];
    int count = read(pipe_ends[0], buff, 10240);
    buff[count] = '\0';
    printf("here goes nothing (count = %d)......\n", count);
    printf("%s", buff);
    return 0;
}

Код должен быть более последовательным в отношении сообщений об ошибках (иногда он использует printf() записать на стандартный вывод; иногда он использует perror() написать в стандартную ошибку) и выход (иногда используя return -1;иногда используя exit(1); - и я добавил return 1; чтобы гарантировать, что дочерний процесс не заканчивает чтение материала, если он не выполняется ls).

Другие вопросы по тегам