Существует ряд проблем с вашим кодом:
- Нет вызовов на
wait()
или эквивалент. - Подключение неправильного конца канала к стандартному входу или стандартному выходу.
- Доступ к неправильным частям набора аргументов - имя команды и аргументы.
- Закрытие неверных файловых дескрипторов для каналов, потенциально многократно.
IЯ думаю, что есть проблемы с утечкой памяти и ненужным выделением памяти в функции customStrCpy()
и функции adjustArray()
, но я не обращал на них внимания - кажется, код работает адекватно, когда вам не нужно беспокоиться о утечках.
Мне нужно было добавить диагностику, чтобы узнать, что ваш код делал неправильно.Следовательно, я использовал код сообщения об ошибках, который доступен в моем репозитории SOQ (Вопросы о переполнении стека) на GitHub в качестве файлов stderr.c
и stderr.h
в подпапке src / libsoq .directory.
Этот код основан на коде, показанном в Revision 6 вопроса - с тех пор код, указанный в вопросе, был изменен.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "stderr.h"
#define MAX_ARGS 20
#define BUFSIZ 1024
static
int get_args(char *cmdline, char *args[])
{
int i = 0;
/* if no args */
if ((args[0] = strtok(cmdline, "\n\t ")) == NULL)
return 0;
while ((args[++i] = strtok(NULL, "\n\t ")) != NULL)
{
if (i >= MAX_ARGS)
{
printf("Too many arguments!\n");
exit(1);
}
}
/* the last one is always NULL */
return i;
}
static
void customStrCpy(char **line1, char *line2)
{
int strlen1 = strlen(*line1), strlen2 = strlen(line2);
if (strlen1 < strlen2)
{
// Creates a dynamically allocated array that is big enough to store the contents of line2.
*line1 = calloc(strlen2, sizeof(char));
}
strcpy(*line1, line2);
}
static
void adjustArray(char *args[], int args_itr, int *nargs)
{
int i, j;
for (i = 0; i < 2; i++)
{
for (j = args_itr; j < (*nargs) - 1; j++)
{
customStrCpy(&args[j], args[j + 1]);
}
args[(*nargs) - 1] = 0;
(*nargs)--;
}
}
static
void process(int *greaterthan, int *d_greaterthan, char *args_pipe[],
int *nargs_p, int *fileno_out, int *fileno_in, int *lessthan,
FILE **fout, FILE **fin)
{
int args_itr;
err_remark("-->> %s():\n", __func__);
for (args_itr = 0; args_itr < *nargs_p; args_itr++)
{
if (strcmp(args_pipe[args_itr], ">") == 0)
{
err_remark("---- %s(): > %s\n", __func__, args_pipe[args_itr + 1]);
*fout = fopen(args_pipe[args_itr + 1], "w");
if (fout == NULL)
err_syserr("failed to open file '%s' for writing\n",
args_pipe[args_itr + 1]);
*fileno_out = fileno(*fout);
*greaterthan = 1;
adjustArray(args_pipe, args_itr, nargs_p);
args_itr--;
}
else if (strcmp(args_pipe[args_itr], ">>") == 0)
{
err_remark("---- %s(): >> %s\n", __func__, args_pipe[args_itr + 1]);
*fout = fopen(args_pipe[args_itr + 1], "a");
if (fout == NULL)
err_syserr("failed to open file '%s' for appending\n",
args_pipe[args_itr + 1]);
*fileno_out = fileno(*fout);
*d_greaterthan = 1;
adjustArray(args_pipe, args_itr, nargs_p);
args_itr--;
}
else if (strcmp(args_pipe[args_itr], "<") == 0)
{
err_remark("---- %s(): < %s\n", __func__, args_pipe[args_itr + 1]);
*fin = fopen(args_pipe[args_itr + 1], "r");
if (fin == NULL)
err_syserr("failed to open file '%s' for reading\n",
args_pipe[args_itr + 1]);
*fileno_in = fileno(*fin);
*lessthan = 1;
adjustArray(args_pipe, args_itr, nargs_p);
args_itr--;
}
}
err_remark("<<-- %s()\n", __func__);
}
static
void execute(char *cmdline)
{
int lessthan = 0;
int greaterthan = 0, d_greaterthan = 0;
int args_itr, pipe_flag = 0;
int flag_count = 0, fileno_in, fileno_out;
char *args_pipe[MAX_ARGS];/*5 and 3 are test numbers.*/
char *args[MAX_ARGS];
int nargs = get_args(cmdline, args);
if (nargs <= 0)
return;
if (!strcmp(args[0], "quit") || !strcmp(args[0], "exit"))
{
exit(0);
}
for (int x = 0; x < nargs; x++)
err_remark("args[%d] = [%s]\n", x, args[x]);
FILE *fout = stdout;
FILE *fin = stdin;
for (args_itr = 0; args_itr < nargs; args_itr++)
{
if (!strcmp(args[args_itr], "|"))
{
pipe_flag = 1;
flag_count++;
}
}
if (pipe_flag)
{
int num_commands = flag_count + 1, i = 0, j = 0;
int fd[flag_count][2];
for (i = 0; i < flag_count; i++)
{
pipe(fd[i]);
err_remark("opened pipe - %d and %d\n", fd[i][0], fd[i][1]);
}
for (i = 0; i < num_commands; i++)
{
int nargs_p = 0, args_pipe_itr = 0;
while (j < nargs && strcmp(args[j], "|"))
{
// Possibly make into for loop.
args_pipe[args_pipe_itr] = args[j];
args_pipe_itr++;
j++;
nargs_p++;
}
args_pipe[nargs_p] = NULL; /* JL: Null-terminate argument list */
j++; /* Skip pipe argument */
int pid = fork();
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
if (pid < 0)
{
err_syserr("failed to fork a child process: ");
/*NOTREACHED*/
}
if (pid > 0)
{
err_remark("Parent launched child %d\n", pid);
continue;
}
else // if (pid == 0)
{
err_remark("child at work (i == %d)\n", i);
if (i == 0)
{
err_remark("first process in pipeline (i = %d, j = %d)\n", i, j);
process(&greaterthan, &d_greaterthan, args_pipe,
&nargs_p, &fileno_out, &fileno_in, &lessthan,
&fout, &fin);
if (lessthan)
dup2(fileno_in, STDIN_FILENO);
dup2(fd[i][1], STDOUT_FILENO);
}
else if (i != num_commands - 1)
{
err_remark("middle process in pipeline (i = %d, j = %d)\n", i, j);
dup2(fd[i - 1][0], STDIN_FILENO); /* JL */
/* Will need to process I/O redirections mid-pipeline */
/* However, they're always a bug in the shell script */
dup2(fd[i][1], STDOUT_FILENO);
}
else
{
err_remark("final process in pipeline (i = %d, j = %d)\n", i, j);
dup2(fd[i - 1][0], STDIN_FILENO); /* JL */
process(&greaterthan, &d_greaterthan, args_pipe,
&nargs_p, &fileno_out, &fileno_in, &lessthan,
&fout, &fin);
if (greaterthan || d_greaterthan)
err_remark("Redirection:\n"),
dup2(fileno_out, STDOUT_FILENO);
}
err_remark("close pipes\n");
int close_pipes;
for (close_pipes = 0; close_pipes < flag_count; close_pipes++)
{
close(fd[close_pipes][0]); /* JL */
close(fd[close_pipes][1]); /* JL */
}
if (fout != stdout)
fclose(fout);
if (fin != stdin)
fclose(fin);
err_remark("execute command [%s]\n", args_pipe[0]);
for (int i = 1; args_pipe[i] != NULL; i++)
err_remark("argument %d [%s]\n", i, args_pipe[i]);
execvp(args_pipe[0], args_pipe);
err_syserr("Failed to execute (i=%d) '%s': ", i, args_pipe[0]);
/*NOTREACHED*/
}// end child.
}
/* Parent process closes its copy of each pipe */
for (i = 0; i < flag_count; i++)
{
close(fd[i][0]);
close(fd[i][1]);
}
return;
}
else
err_remark("No pipe in input - no command executed\n");
}
int main(int argc, char **argv)
{
if (argc >= 0)
err_setarg0(argv[0]);
err_setlogopts(ERR_PID|ERR_MILLI);
for ( ; ; )
{
printf("COP4338$ ");
char cmdline[BUFSIZ];
if (fgets(cmdline, BUFSIZ, stdin) == NULL)
{
printf("EOF\n");
exit(1);
}
execute(cmdline);
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
err_remark("PID %d exited with status 0x%.4X\n", corpse, status);
}
}
Функция process()
не должна различать >
и >>
в списке аргументов;было бы достаточно правильно обработать стандартный вывод.Код вызова будет проще.Я не уверен, что вам следует использовать fopen()
- было бы разумнее использовать open()
с соответствующими аргументами управления.
Однако, по крайней мере, это работает.Исходный код был shell61.c
, скомпилированный в программу shell61
.
$ shell61
COP4338$ sort < shell61.c | grep main | cat > o.txt
shell61: 2019-05-04 22:17:39.982 - pid=8150: args[0] = [sort]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[1] = [<]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[2] = [shell61.c]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[3] = [|]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[4] = [grep]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[5] = [main]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[6] = [|]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[7] = [cat]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[8] = [>]
shell61: 2019-05-04 22:17:39.983 - pid=8150: args[9] = [o.txt]
shell61: 2019-05-04 22:17:39.983 - pid=8150: opened pipe - 3 and 4
shell61: 2019-05-04 22:17:39.983 - pid=8150: opened pipe - 5 and 6
shell61: 2019-05-04 22:17:39.984 - pid=8150: Parent launched child 8153
shell61: 2019-05-04 22:17:39.984 - pid=8150: Parent launched child 8154
shell61: 2019-05-04 22:17:39.984 - pid=8150: Parent launched child 8155
shell61: 2019-05-04 22:17:39.984 - pid=8154: child at work (i == 1)
shell61: 2019-05-04 22:17:39.984 - pid=8153: child at work (i == 0)
shell61: 2019-05-04 22:17:39.984 - pid=8155: child at work (i == 2)
shell61: 2019-05-04 22:17:39.985 - pid=8154: middle process in pipeline (i = 1, j = 7)
shell61: 2019-05-04 22:17:39.985 - pid=8155: final process in pipeline (i = 2, j = 11)
shell61: 2019-05-04 22:17:39.985 - pid=8153: first process in pipeline (i = 0, j = 4)
shell61: 2019-05-04 22:17:39.985 - pid=8154: close pipes
shell61: 2019-05-04 22:17:39.985 - pid=8155: -->> process():
shell61: 2019-05-04 22:17:39.985 - pid=8153: -->> process():
shell61: 2019-05-04 22:17:39.986 - pid=8154: execute command [grep]
shell61: 2019-05-04 22:17:39.986 - pid=8155: ---- process(): > o.txt
shell61: 2019-05-04 22:17:39.986 - pid=8153: ---- process(): < shell61.c
shell61: 2019-05-04 22:17:39.986 - pid=8154: argument 1 [main]
shell61: 2019-05-04 22:17:39.986 - pid=8155: <<-- process()
shell61: 2019-05-04 22:17:39.986 - pid=8153: <<-- process()
shell61: 2019-05-04 22:17:39.987 - pid=8155: Redirection:
shell61: 2019-05-04 22:17:39.987 - pid=8153: close pipes
shell61: 2019-05-04 22:17:39.987 - pid=8155: close pipes
shell61: 2019-05-04 22:17:39.987 - pid=8153: execute command [sort]
shell61: 2019-05-04 22:17:39.988 - pid=8155: execute command [cat]
shell61: 2019-05-04 22:17:39.993 - pid=8150: PID 8153 exited with status 0x0000
shell61: 2019-05-04 22:17:39.993 - pid=8150: PID 8154 exited with status 0x0000
shell61: 2019-05-04 22:17:39.994 - pid=8150: PID 8155 exited with status 0x0000
COP4338$ cat o.txt | cat
shell61: 2019-05-04 22:18:08.339 - pid=8150: args[0] = [cat]
shell61: 2019-05-04 22:18:08.339 - pid=8150: args[1] = [o.txt]
shell61: 2019-05-04 22:18:08.339 - pid=8150: args[2] = [|]
shell61: 2019-05-04 22:18:08.339 - pid=8150: args[3] = [cat]
shell61: 2019-05-04 22:18:08.339 - pid=8150: opened pipe - 3 and 4
shell61: 2019-05-04 22:18:08.340 - pid=8150: Parent launched child 8157
shell61: 2019-05-04 22:18:08.340 - pid=8150: Parent launched child 8158
shell61: 2019-05-04 22:18:08.340 - pid=8157: child at work (i == 0)
shell61: 2019-05-04 22:18:08.340 - pid=8158: child at work (i == 1)
shell61: 2019-05-04 22:18:08.340 - pid=8157: first process in pipeline (i = 0, j = 3)
shell61: 2019-05-04 22:18:08.341 - pid=8157: -->> process():
shell61: 2019-05-04 22:18:08.341 - pid=8158: final process in pipeline (i = 1, j = 5)
shell61: 2019-05-04 22:18:08.341 - pid=8157: <<-- process()
shell61: 2019-05-04 22:18:08.341 - pid=8158: -->> process():
shell61: 2019-05-04 22:18:08.342 - pid=8157: close pipes
shell61: 2019-05-04 22:18:08.342 - pid=8158: <<-- process()
shell61: 2019-05-04 22:18:08.342 - pid=8157: execute command [cat]
shell61: 2019-05-04 22:18:08.342 - pid=8158: close pipes
shell61: 2019-05-04 22:18:08.343 - pid=8157: argument 1 [o.txt]
shell61: 2019-05-04 22:18:08.343 - pid=8158: execute command [cat]
int main(int argc, char **argv)
shell61: 2019-05-04 22:18:08.347 - pid=8150: PID 8158 exited with status 0x0000
shell61: 2019-05-04 22:18:08.347 - pid=8150: PID 8157 exited with status 0x0000
COP4338$ ls
shell61: 2019-05-04 22:18:41.516 - pid=8150: args[0] = [ls]
shell61: 2019-05-04 22:18:41.516 - pid=8150: No pipe in input - no command executed
COP4338$ exit
$
Когда я сначала добавил код wait()
, я получил такие сообщения:
$ shell61
COP4338$ ls | grep shell
shell61: 2019-05-04 14:29:23.201 - pid=5181: args[0] = [ls]
shell61: 2019-05-04 14:29:23.202 - pid=5181: args[1] = [|]
shell61: 2019-05-04 14:29:23.202 - pid=5181: args[2] = [grep]
shell61: 2019-05-04 14:29:23.202 - pid=5181: args[3] = [shell]
shell61: 2019-05-04 14:29:23.202 - pid=5181: opened pipe - 3 and 4
Process ID: 5182
Process ID: 5183
Process ID: 0
Process ID: 0
shell61: 2019-05-04 14:29:23.203 - pid=5183: child at work
PID 5183 exited with status 0x000B
shell61: 2019-05-04 14:29:23.203 - pid=5182: child at work
PID 5182 exited with status 0x000D
COP4338$ exit
$
статус выхода из 5183 был 0x000B, что означает «умер от сигнала 11 - SIGSEGV»;статус выхода из 5182 был 0x000D, что переводится как «сигнал отмершей формы 13 - SIGPIPE».Это была важная информация.Добавленная печать, в частности, через err_remark()
, помогла показать, где были проблемы.Обнаружение того, что вы звоните process()
с &args_pipe[i]
вместо &args_pipe[0]
или args_pipe
, также было ключевым.
Когда трасса выглядела так:
$ shell61
COP4338$ ls | grep shell
shell61: 2019-05-04 16:24:24.297 - pid=6417: args[0] = [ls]
shell61: 2019-05-04 16:24:24.298 - pid=6417: args[1] = [|]
shell61: 2019-05-04 16:24:24.298 - pid=6417: args[2] = [grep]
shell61: 2019-05-04 16:24:24.298 - pid=6417: args[3] = [shell]
shell61: 2019-05-04 16:24:24.298 - pid=6417: opened pipe - 3 and 4
shell61: 2019-05-04 16:24:24.299 - pid=6417: Parent launched child 6419
shell61: 2019-05-04 16:24:24.299 - pid=6417: Parent launched child 6420
shell61: 2019-05-04 16:24:24.300 - pid=6420: child at work (i == 1)
shell61: 2019-05-04 16:24:24.299 - pid=6419: child at work (i == 0)
shell61: 2019-05-04 16:24:24.300 - pid=6420: final process in pipeline (i = 1, j = 5)
shell61: 2019-05-04 16:24:24.301 - pid=6419: first process in pipeline (i = 0, j = 2)
shell61: 2019-05-04 16:24:24.301 - pid=6420: -->> process():
shell61: 2019-05-04 16:24:24.302 - pid=6420: <<-- process()
greaterthan = 0
d_greaterthan = 0
shell61: 2019-05-04 16:24:24.302 - pid=6419: -->> process():
shell61: 2019-05-04 16:24:24.302 - pid=6420: close pipes
shell61: 2019-05-04 16:24:24.302 - pid=6419: <<-- process()
Lessthan = 0
shell61: 2019-05-04 16:24:24.303 - pid=6420: execute command [grep]
shell61: 2019-05-04 16:24:24.303 - pid=6420: argument 1 [shell]
shell61: 2019-05-04 16:24:24.303 - pid=6419: close pipes
shell61: 2019-05-04 16:24:24.304 - pid=6419: execute command [ls]
grep: (standard input): Bad file descriptor
PID 6420 exited with status 0x0100
PID 6419 exited with status 0x000D
COP4338$ exit
$
было ясно, что была проблема с перенаправлением - не было ясно, был ли стандартный ввод закрыт или если grep
былпопросить прочитать из файлового дескриптора только для записи;Проверка кода показала, что последняя была на самом деле проблемой (с использованием fd[i][1]
, где нужно было использовать fd[i][0]
).
Я не могу не подчеркнуть, насколько полезны мощные, но простые инструменты сообщения об ошибках.Я использую код stderr.c
и stderr.h
в большинстве своих программ - в крайнем случае я редко их использую.Вам следует либо приобрести их копию, либо придумать собственный эквивалент, но имейте в виду, что этот код доработан за 30 лет (хотя были некоторые годы, когда я не вносил в него какие-либо изменения - 2018 год - самый последний такой год)., но это не изменилось в 1992-1995, и в последующие годы.Самая ранняя версия, которая у меня есть, была датирована 1988 годом;код был действительно запущен до этого, но у меня не получилось успешно перенести данные при перемещении заданий (8 "дискета оказалась плохим выбором).