我正在尝试编写一个简单的shell(只是试图了解shell是如何工作的)。第一部分(解析命令行参数)可以正常工作,但是第二部分(执行命令)无法正常工作。
我正在尝试为每两个连续的程序创建一对管道。当前命令写入pdes [0],下一个从pdes [1]读取它。由于我要在一个循环中创建多个进程,因此使用一个名为prev_out的变量来记住列表中上一个命令的读取端。列表中的第一个命令从stdin而不是管道读取其输入。同样,最后一个程序还将其输出写入标准输出,而不是管道。
pipe1 pipe2
* * * *
* * * *
--> CMD1 CMD2 CMD3 --> stdout
类似上面的安排。每个命令均写入pdes [0],并读取对应的pdes [1]。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAXLINE 255
#define MAXARG 32
#define MAXNCMD 16
extern char **environ;
int
main(int argc, char *argv[])
{
char *cmds[MAXNCMD][MAXARG];
char line [MAXLINE + 1];
pid_t childs [MAXNCMD];
char *cmdcur, *argcur;
char *linecp;
int c , st;
int i , j, k;
int prev_in , prev_out;
for (;;) {
printf("%% ");
i = 0;
while ('\n' != (c = getc(stdin)) && i < MAXLINE) {
if (c == EOF)
exit(EXIT_SUCCESS);
/* NOTREACHED */
line[i++] = c;
}
line[i] = 0; /* null-terminate */
if (0 == strncmp(line, "exit", 4)) {
exit(EXIT_SUCCESS);
/* NOTREACHED */
}
if (0 == strncmp(line, "cd ", 3)) {
if (-1 == chdir(line + 3)) {
perror("chdir()");
}
continue;
}
linecp = line;
j = 0;
k = 0;
while (NULL != (cmdcur = strsep(&linecp, "|"))) {
while (*cmdcur == ' ')
cmdcur++;
k = 0;
while (NULL != (argcur = strsep(&cmdcur, " "))) {
while (*argcur == ' ')
argcur++;
if (NULL == (cmds[j][k] = malloc(strlen(argcur) + 1))) {
perror("malloc()");
exit(EXIT_FAILURE);
}
strncpy(cmds[j][k], argcur, strlen(argcur));
k++;
if (k == MAXARG) {
fprintf(stderr, "%s: Too many args\n", argv[0]);
exit(EXIT_FAILURE);
} /* NOTREACHED */
}
cmds[j][k] = NULL;
j++;
if (j == MAXNCMD) {
fprintf(stderr, "%s: Too many args\n", argv[0]);
exit(EXIT_FAILURE);
/* NOTREACHED */
}
}
*cmds[j] = NULL;
int nc = j;
for (int m = 0; m < nc; m++) {
int pdes [2];
if (-1 == pipe(pdes)) {
perror("pipe()");
exit(EXIT_FAILURE);
}
if (-1 == (childs[m] = fork())) {
perror("fork()");
exit(EXIT_FAILURE);
}
prev_out = pdes[1];
if (0 == childs[m]) {
/* child */
if (0 == m) {
close(STDOUT_FILENO); /* the first process in the pipe group needs
dup(pdes[0]); * only to set its stdout, the next process
prev_out = pdes[1]; * will use the other end */
} else if (m == (nc - 1)) {
close(STDIN_FILENO); /* the last process only needs to set its stdin
dup(prev_out); * it will use termical as stdout by default */
} else {
close(STDIN_FILENO); /* inner processes should set both stdin and stdout */
dup(prev_out);
close(STDOUT_FILENO);
dup(pdes[0]);
prev_out = pdes[1];
}
if (-1 == execvp(cmds[i][0], cmds[i])) {
perror("execvp()");
exit(EXIT_FAILURE);
}
} else if (childs[m] > 0) {
if (childs[m] != wait(NULL)) {
perror("wait()");
exit(EXIT_FAILURE);
}
}
}
}
return 0;
}