gpt4 book ai didi

c - 制作 Linux shell 时的流重定向和管道

转载 作者:太空狗 更新时间:2023-10-29 15:04:50 26 4
gpt4 key购买 nike

我有一项任务是用 C 语言创建一个 Linux shell。目前,我一直致力于实现重定向和管道。我到目前为止的代码如下。 main() 解析用户的输入。如果命令是内置的,则执行该命令。否则,标记化的输入将传递给 execute()(我知道我可能应该将内置命令拉入它们自己的函数中)。

execute() 的作用是遍历数组。如果遇到< , > , 或 |它应该采取适当的行动。我试图正确工作的第一件事是管道。不过,我肯定做错了什么,因为我什至不能让它为一根管道工作。例如,示例输入/输出:

/home/ad/Documents> ls -l | grep sh
|: sh: No such file or directory
|

我的想法是让每个方向和管道仅针对一种情况起作用,然后通过使函数递归,我希望可以在同一命令行中使用多个重定向/管道。例如,我可以做 program1 < input1.txt > output1.txtls -l | grep sh > output2.txt .

我希望有人能指出我在尝试管道时的错误,并可能就如何处理用户输入多个重定向/管道的情况提供一些指导。

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

int MAX_PATH_LENGTH = 1024; //Maximum path length to display.
int BUF_LENGTH = 1024; // Length of buffer to store user input
char * delims = " \n"; // Delimiters for tokenizing user input.
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;

void execute(char **argArray){

char **pA = argArray;
int i = 0;
while(*pA != NULL) {
if(strcmp(argArray[i],"<") == 0) {
printf("<\n");
}
else if(strcmp(argArray[i],">") == 0) {
printf(">\n");
}
else if(strcmp(argArray[i],"|") == 0) {
int fds[2];
pipe(fds);
pid_t pid;
if((pid = fork()) == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
char** argList;
memcpy(argList, argArray, i);
execvp(argArray[0], argArray);
}
if((pid = fork()) == 0) {
dup2(fds[PIPE_READ], 0);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
execvp(argArray[i+1], pA);
}
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(NULL);
wait(NULL);
printf("|\n");
}
else {
if(pid == 0){
execvp(argArray[0], argArray);
printf("Command not found.\n");
}
else
wait(NULL);*/
}
*pA++;
i++;
}
}

int main () {

char path[MAX_PATH_LENGTH];
char buf[BUF_LENGTH];
char* strArray[BUF_LENGTH];
/**
* "Welcome" message. When mash is executed, the current working directory
* is displayed followed by >. For example, if user is in /usr/lib/, then
* mash will display :
* /usr/lib/>
**/
getcwd(path, MAX_PATH_LENGTH);
printf("%s> ", path);
fflush(stdout);

/**
* Loop infinitely while waiting for input from user.
* Parse input and display "welcome" message again.
**/
while(1) {
fgets(buf, BUF_LENGTH, stdin);
char *tokenPtr = NULL;
int i = 0;
tokenPtr = strtok(buf, delims);

if(strcmp(tokenPtr, "exit") == 0){

exit(0);
}
else if(strcmp(tokenPtr, "cd") == 0){
tokenPtr = strtok(NULL, delims);
if(chdir(tokenPtr) != 0){
printf("Path not found.\n");
}
getcwd(path, MAX_PATH_LENGTH);
}
else if(strcmp(tokenPtr, "pwd") == 0){
printf("%s\n", path);
}
else {
while(tokenPtr != NULL) {
strArray[i++] = tokenPtr;
tokenPtr = strtok(NULL, delims);
}
execute(strArray);
}

bzero(strArray, sizeof(strArray)); // clears array
printf("%s> ", path);
fflush(stdout);
}

}

最佳答案

部分问题出在管道处理代码中 - 如您所料。

else if (strcmp(argArray[i], "|") == 0) {
int fds[2];
pipe(fds);
pid_t pid;
if ((pid = fork()) == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
char** argList;
memcpy(argList, argArray, i);
execvp(argArray[0], argArray);
}
if ((pid = fork()) == 0) {
dup2(fds[PIPE_READ], 0);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
execvp(argArray[i+1], pA);
}
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(NULL);
wait(NULL);
printf("|\n");
}

第一个 execvp() 可能打算使用 argList,因为您刚刚在那里复制了一些 Material 。但是,您复制了 i 字节,而不是 i 字符指针,并且您没有确保管道被 zapped 和 替换为空指针。

memcpy(argList, argArray, i * sizeof(char *));
argList[i] = 0;
execvp(argList[0], argList);

请注意,这还没有验证argList 上没有缓冲区溢出; 请注意,没有为argList 分配空间;如果你使用它,你应该在执行 memcpy() 之前分配内存。

或者,更简单的是,您可以不使用副本。由于您处于子进程中,您可以简单地 zapargArray[i] 替换为空指针,而不会影响父进程或其他子进程:

argArray[i] = 0;
execvp(argArray[0], argArray);

您可能还注意到 execvp() 的第二次调用使用了一个看不见的变量 pA;它几乎肯定是错误初始化的。作为一个比较好的经验法则,你应该写:

execvp(array[n], &array[n]);

上面的调用不符合这个模式,但如果你遵循它,你就不会出错。

您还应该有基本的错误报告和 exit(1)(或者可能是 _exit(1)_Exit(1))在每个 execvp() 之后,这样子进程在执行失败时就不会继续。 execvp() 没有成功返回,但 execvp() 肯定可以返回。

现在,这些对 execvp() 的调用大概应该是您进行递归调用的地方。在尝试处理其他 I/O 重定向之前,您需要先处理管道。请注意,在标准 shell 中,您可以执行以下操作:

> output < input command -opts arg1 arg2

这是一种常规用法,但实际上是允许的。

一件好事 - 您已确保 pipe() 中的原始文件描述符在所有三个进程(父进程和两个子进程)中都已关闭。这是您避免犯的常见错误;干得好。

关于c - 制作 Linux shell 时的流重定向和管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7549637/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com