#c #unix #pipe #fork
#c #unix #канал #fork
Вопрос:
Я работаю над написанием оболочки на C для целей обучения, и я пытаюсь разрешить переменное количество каналов. В целом, похоже, это отлично работает. Но я заметил проблему с wc
командой.
Когда я передаю какой-либо вывод другой программы в wc
like ls | wc
, он всегда возвращает
1 3 35
независимо от того, что я передаю в него. Другие команды работают так, как ожидалось, когда я подключаюсь к ним. В моей обычной zsh
оболочке wc
работает нормально. Я изо всех сил пытаюсь найти проблему. Я пробовал добавлять waitpid
после вилок, но без кубиков.
Вот основной цикл оболочки в main
функции:
while (1) {
printf("33[31;1mshell:33[0m ");
line = read_cmds();
if (strstr(line, "|")) {
// check for pipes first
pipe_exec(line);
} else {
// we have a single command
tokens = split(line, " trn");
if (*tokens != NULL) shell_exec(tokens);
free(tokens);
}
}
Вот функция, которая перебирает команды:
void pipe_exec(char *line)
{
int in, status;
int pipe_no; // keep track of ptr to bring it back to free
int pfd[2];
pid_t rc;
char **cmd, **pipe_cmds;
// split takes a string and splits into array of strings based on delimiter
pipe_cmds = split(line, "|");
in = 0;
pipe_no = 0;
while (*pipe_cmds) {
cmd = split(*pipe_cmds, " trn");
if (pipe(pfd) < 0) perror("pipe");
make_proc(in, pfd[1], cmd);
close(pfd[1]);
in = pfd[0];
pipe_cmds ; // move ptr ahead one
pipe_no ;
}
// move pointer back and free
pipe_cmds -= pipe_no;
free(pipe_cmds);
rc = fork();
if (rc == 0) {
if (in != 0) dup2(in, STDIN_FILENO);
execvp(*cmd, cmd);
}
}
А затем make_proc
функция, которую вызывает вышеупомянутая функция:
void make_proc(int in, int out, char **cmd)
{
pid_t rc;
rc = fork();
if (rc == 0) {
if (in != STDIN_FILENO) {
dup2(in, STDIN_FILENO);
close(in);
}
if (out != STDOUT_FILENO) {
dup2(out, STDOUT_FILENO);
close(out);
}
execvp(*cmd, cmd);
}
}
Я убрал часть проверки ошибок, чтобы сэкономить место здесь.
Любая помощь приветствуется!
Комментарии:
1. Попробуйте использовать
ls | cat
, чтобы увидеть, какие входные данные получает второй процесс.2. @barmar
ls | cat
, похоже, работает правильно. Он возвращает все 7 элементов в каталоге. Я пыталсяls | cat | wc
, но безрезультатно. Я продолжу работать над этим. Спасибо.3. Я не могу придумать ни одной причины
wc
, по которой это работало бы по-другому.4. Попробуйте использовать
/usr/bin/wc
5. Я предлагаю использовать трассировку системных вызовов в
wc
процессе, чтобы увидеть, что он на самом деле читает.
Ответ №1:
Вы выполняете последнюю команду дважды и передаете ее первый экземпляр второму. Добавление чего-то вроде:
while (*pipe_cmds) {
cmd = split(*pipe_cmds, " trn");
if (!pipe_cmds[1]) {
break;
}
if (pipe(pfd) < 0) perror("pipe");
make_proc(in, pfd[1], cmd);
close(pfd[1]);
in = pfd[0];
pipe_cmds ; // move ptr ahead one
pipe_no ;
}
предотвратило бы ненужный экземпляр, хотя я бы предпочел немного переработать эту функцию.
Комментарии:
1. Да, это делает это! Спасибо. Да, я согласен. Теперь, когда я знаю, что происходит, я проведу рефакторинг.