gpt4 book ai didi

在 C 中创建一个处理重定向和管道的 shell

转载 作者:太空宇宙 更新时间:2023-11-04 04:23:52 24 4
gpt4 key购买 nike

下面的函数成功执行了任何不包含管道的命令,所以不用担心奇怪的函数。这些工作。我遇到的问题是每当我执行如下命令时:

cat file.txt | grep string

该命令已成功执行,但它仍处于空闲状态,因此不知何故它卡住了,无法执行其他命令。为什么会这样?我认为这与我使用 pipe、dup 和 fork 的方式有关,所以尝试从这些功能来解决问题。我知道您可能会争辩说此代码不适用于其他带有管道的命令,但我只是想让这个特定示例起作用,为此我只是在第一次迭代中将 STDIN 重定向到打开的文件。

    int myshell_execute(struct processNode* list, int a)
{

struct processNode* myList = list; // next node to be handled
int pipefd[2];
int in=0;
if (pipe(pipefd) == -1) {
perror("pipe");
myshell_exit(-1);
}

while(myList != NULL)
{

char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
int *status;
int* stdout;
int stdout_num = 1;
stdout = &stdout_num;
int fileDescriptor;
pid_t pid;

if(strcmp(program,"cd") == 0)
{
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0)
{
return myshell_exit(0);
}


pid = fork();

if(pid == 0)
{

if(in == 1)
{
close(pipefd[1]);
dup2(pipefd[0],0);
close(pipefd[0]);
}

if(sizeOfLine(redirection_string) != 0)
{
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}

if(*stdout == 1 && myList->next !=NULL)
{
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO); // with this
close(pipefd[1]);
}
if(execvp(program,program_args) !=-1)
{
perror("myshell:");
myshell_exit(-1);
}
else{
myshell_exit(0);
}
}
else if (pid <0)
{
perror("myshell: ");
myshell_exit(-1);
}
else
{
wait(status);

}
in = 1;
myList = myList->next;

}
}

新的解决方案:

int helper_execute(int in, int out, char* program, char ** program_args, char *redirection)
{
pid_t pid;

if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}

if (out != 1)
{
dup2 (out, 1);
close (out);
}

redirectionHandler(redirection);
return execvp (program, program_args);
}

return pid;
}


int myshell_execute(struct processNode* list, int a)
{
int i;
pid_t pid;
int in, fd [2];
struct processNode*new_list = list;
char ** newProgram = new_list->program_arguments;
char ** redirection = new_list->redirection;
char * program = new_list->program;

/* The first process should get its input from the original file descriptor 0. */
in = 0;

/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
while(new_list->next != NULL)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
helper_execute (in, fd [1],program,newProgram,redirection);

/* No need for the write end of the pipe, the child will write here. */
close (fd [1]);

/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];

new_list = new_list->next;
}

/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);

/* Execute the last stage with the current process. */
char* lastProgram = new_list->program;
char ** lastRedirection = new_list->redirection;
char * lastPrArguments = new_list->program_arguments;
redirectionHandler(redirection);
return execvp (lastProgram, lastPrArguments);
}

int main() {
int i=0;
char **input;
struct processNode* list;
int tracker = 0;

while ((input = getline()) != EOF) {
list = create_list(input);
myshell_execute(list,0);
}

return 0;
}

这个解决方案的唯一问题是,只要执行一个命令,main 就会立即检测到文件的末尾,因此它会退出 shell。

最佳答案

这是因为

  1. 你的父进程也保持管道打开,
  2. 就在 fork 之后调用wait。如果您的第一个进程 (cat) 填满了它的输出管道,并且还没有任何进程可用于使用管道的读取端,那么该进程将永远停止。

看起来像这样:

#define MAX_PIPE_LEN 256
int myshell_execute(struct processNode* list, int a)
{
/* fd0 are the input and output descriptor for this command. fd1
are the input and output descriptor for the next command in the
pipeline. */
int fd0[2] = { STDIN_FILENO, STDOUT_FILENO },
fd1[2] = { -1, -1 };
pid_t pids[MAX_PIPE_LEN] = { 0 };
int pipe_len;
struct processNode* myList; // next node to be handled
int status;
int failed = 0;
for (pipe_len = 0, myList = list;
pipe_len < MAX_PIPE_LEN && myList != NULL;
pipe_len++, myList = myList->next) {
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
if(strcmp(program,"cd") == 0) {
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0) {
return myshell_exit(0);
}
if (myList->next != NULL) {
/* The output of this command is piped into the next one */
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
failed = 1;
break;
}
fd1[0] = pipefd[0];
fd1[1] = fd0[1];
fd0[1] = pipefd[1];
}
pids[pipe_len] = fork();
if (pids[pipe_len] < 0) {
perror("error: fork failed");
failed = 1;
break;
}
if (pids[pipe_len] == 0) {
if (fd0[0] != STDIN_FILENO) {
if (dup2(fd0[0], STDIN_FILENO) == -1) {
perror("error: dup2 input failed");
abort();
}
close(fd0[0]);
}
if (fd0[1] != STDOUT_FILENO) {
if (dup2(fd0[1], STDOUT_FILENO) == -1) {
perror("error: dup2 outut failed");
abort();
}
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0) {
close(fd1[1]);
}
if(sizeOfLine(redirection_string) != 0) {
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
execvp(program, program_args);
perror("error: execvp failed");
abort();
}
if (fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd1[1] != STDOUT_FILENO) {
close(fd0[1]);
}
fd0[0] = fd1[0];
fd0[1] = fd1[1];
fd1[0] = fd1[1] = -1;
}
if (myList->next) {
fprintf(stderr, "ERROR: MAX_PIPE_LEN (%d) is too small\n",
MAX_PIPE_LEN);
}

if (fd0[0] >= 0 && fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd0[1] >= 0 && fd0[1] != STDOUT_FILENO) {
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0 && fd1[1] != STDOUT_FILENO) {
close(fd1[1]);
}
/* Now wait for the commands to finish */
int i;
for (i = 0; i < pipe_len; i++) {
if (waitpid(pids[pipe_len - 1], &status, 0) == -1) {
perror("error: waitpid failed");
failed = 1;
}
}
if (failed)
status = -1;
myshell_exit(status);
}

关于在 C 中创建一个处理重定向和管道的 shell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43568345/

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