Используя 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 ответа
У вас было три проблемы:
Из справочной страницы: массив
pipefd
используется для возврата двух файловых дескрипторов, относящихся к концам канала.pipefd[0]
относится к концу чтения канала.pipefd[1]
относится к концу записи канала. Вы использовалиpipefd[0]
написать иpipefd[1]
читать. Это не сработает. Это было основной причинойEBADF
(неверный дескриптор файла) ошибка. Если вы хотите двунаправленность, используйтеsocketpair()
Когда ты
fork()
, вам нужно закрыть файловые дескрипторы, которые вам не нужны. Это другой конец трубы.Я считаю, что вы должны
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
).