用C管道的Shell程序

时间:2015-11-25 08:51:24

标签: c linux shell

管道有问题。我的程序是C语言中的Shell程序。我想执行例如ls | wc,但运行后得到的是:

  

ls:无法访问|:没有这样的文件或目录ls:无法访问wc:没有这样的文件或目录。

我做错了什么?

#include <stdlib.h>   
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>

#define MAX_CMD_LENGTH 100

#define MAX_NUM_PARAMS 10

int parsecmd(char* cmd, char** params) { //split cmd into array of params
    int i,n=-1;
    for(i=0; i<MAX_NUM_PARAMS; i++) {
        params[i] = strsep(&cmd, " ");
        n++;
        if(params[i] == NULL) break;
    }
    return(n);
};

int executecmd(char** params) {
    pid_t pid = fork(); //fork process

    if (pid == -1) { //error
        char *error = strerror(errno);
        printf("error fork!!\n");
        return 1;
    } else if (pid == 0) { // child process
        execvp(params[0], params); //exec cmd
        char *error = strerror(errno);
        printf("unknown command\n");
        return 0;
    } else { // parent process
        int childstatus;
        waitpid(pid, &childstatus, 0);    
        return 1;
    }
};

int execpipe (char ** argv1, char ** argv2) {
    int fds[2];
    pipe(fds);
    int i;
    pid_t pid = fork();
    for (i=0; i<2; i++) {
        if (pid == -1) { //error
            char *error = strerror(errno);
            printf("error fork!!\n");
            return 1;
         } else
             if (pid == 0) {
                 if(i ==0){
                     close(fds[1]);
                     dup2(fds[0], 0);
                     close(fds[0]);
                     execvp(argv1[0], argv1);
                     char *error = strerror(errno);
                     printf("unknown command\n");
                     return 0;
                 } else if(i == 1) { 
                     close(fds[0]);
                     dup2(fds[1], 1);
                     close(fds[1]);
                     execvp(argv2[0], argv2);
                     char *error = strerror(errno);
                     printf("unknown command\n");
                     return 0;
                 }
            } else { // parent process
                int childstatus;
                waitpid(pid, &childstatus, 0);
                return 1;
            }
    } // end for
};


int main() {    
    char cmd[MAX_CMD_LENGTH+1];    
    char * params[MAX_NUM_PARAMS+1];    
    char * argv1[MAX_NUM_PARAMS+1];    
    char * argv2[MAX_NUM_PARAMS+1];    
    int k, y, x;    
    int f = 1;    
    while(1) {
        printf("$"); //prompt    
        if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit       
        if(cmd[strlen(cmd)-1] == '\n') { //remove newline char    
            cmd[strlen(cmd)-1] = '\0';    
        }    
        int j=parsecmd(cmd, params); //split cmd into array of params           
        if (strcmp(params[0], "exit") == 0) break; //exit   
        for (k=0; k <j; k++) { //elegxos gia uparksi pipes    
            if (strcmp(params[k], "|") == 0) {    
                f = 0; y = k;      
               printf("pipe found\n");
            }               
        }
        if (f==0) {
            for (x=0; x<k; x++) {    
               argv1[x]=params[x];
            }     
            int z = 0;     
            for (x=k+1; x< j; x++) {     
                argv2[z]=params[x];
                z++;
            }     
            if (execpipe(argv1, argv2) == 0) break;    
         } else if (f==1) {     
             if (executecmd(params) == 0) break;
         }
    } // end while
    return 0;
}

2 个答案:

答案 0 :(得分:1)

execv*过程不解释shell脚本字符串。它只是启动一个可执行文件并将一个参数数组传递给它。因此,它无法组织管道。

如果您需要执行“普通”shell命令,则可能需要使用system(char*)过程而不是execvp。

否则,如果您需要自己管道,可能需要用'|'解析字符串特殊字符并使用pipe(),fork()和I / O重定向。像这里How to run a command using pipe?

答案 1 :(得分:1)

通过以下更正更新了您的代码。

  1. for()调用后,删除了两次迭代的fork()循环。
  2. dup2调用父进程和子进程后,删除了管道FD的错误关闭。
  3. 根据dup2()调用父和子的重复文件描述符,对齐需要运行的命令。基本上我需要交换execvp(argv2[0], argv2)execvp(argv1[0], argv1)来电。
  4. 在for循环中添加了break;语句,用于搜索竖线字符。
  5. 更新的代码如下。

    #include <stdlib.h>   
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    #define MAX_CMD_LENGTH 100
    
    #define MAX_NUM_PARAMS 10
    
    int parsecmd(char* cmd, char** params) { //split cmd into array of params
        int i,n=-1;
        for(i=0; i<MAX_NUM_PARAMS; i++) {
            params[i] = strsep(&cmd, " ");
            n++;
            if(params[i] == NULL) break;
        }
        return(n);
    };
    
    int executecmd(char** params) {
        pid_t pid = fork(); //fork process
    
        if (pid == -1) { //error
            char *error = strerror(errno);
            printf("error fork!!\n");
            return 1;
        } else if (pid == 0) { // child process
            execvp(params[0], params); //exec cmd
            char *error = strerror(errno);
            printf("unknown command\n");
            return 0;
        } else { // parent process
            int childstatus;
            waitpid(pid, &childstatus, 0);    
            return 1;
        }
    };
    
    int execpipe (char ** argv1, char ** argv2) {
        int fds[2];
        pipe(fds);
        int i;
        pid_t pid = fork();
        if (pid == -1) { //error
            char *error = strerror(errno);
            printf("error fork!!\n");
            return 1;
        } 
        if (pid == 0) { // child process
            close(fds[1]);
            dup2(fds[0], 0);
            //close(fds[0]);
            execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
            char *error = strerror(errno);
            printf("unknown command\n");
            return 0;
        } else { // parent process
            close(fds[0]);
            dup2(fds[1], 1);
            //close(fds[1]);
            execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
            char *error = strerror(errno);
            printf("unknown command\n");
            return 0;
        }
    };
    
    
    int main() {    
        char cmd[MAX_CMD_LENGTH+1];    
        char * params[MAX_NUM_PARAMS+1];    
        char * argv1[MAX_NUM_PARAMS+1] = {0};    
        char * argv2[MAX_NUM_PARAMS+1] = {0};    
        int k, y, x;    
        int f = 1;    
        while(1) {
            printf("$"); //prompt    
            if(fgets(cmd, sizeof(cmd), stdin) == NULL) break; //read command, ctrl+D exit       
            if(cmd[strlen(cmd)-1] == '\n') { //remove newline char    
                cmd[strlen(cmd)-1] = '\0';    
            }    
            int j=parsecmd(cmd, params); //split cmd into array of params           
            if (strcmp(params[0], "exit") == 0) break; //exit   
            for (k=0; k <j; k++) { //elegxos gia uparksi pipes    
                if (strcmp(params[k], "|") == 0) {    
                    f = 0; y = k;      
                   printf("pipe found\n");
                   break;
                }               
            }
            if (f==0) {
                for (x=0; x<k; x++) {    
                   argv1[x]=params[x];
                }     
                int z = 0;     
                for (x=k+1; x< j; x++) {     
                    argv2[z]=params[x];
                    z++;
                }     
                if (execpipe(argv1, argv2) == 0) break;    
             } else if (f==1) {     
                 if (executecmd(params) == 0) break;
             }
        } // end while
        return 0;
    }
    

    如果您只对我所做的更改感兴趣,请参阅以下代码与上述更新代码之间的区别:

    --- original.c
    +++ updated.c
    @@ -4,6 +4,7 @@
     #include <unistd.h>
     #include <errno.h>
     #include <sys/types.h>
    +#include <sys/wait.h>
    
     #define MAX_CMD_LENGTH 100
    
    @@ -43,44 +44,36 @@
         pipe(fds);
         int i;
         pid_t pid = fork();
    -    for (i=0; i<2; i++) {
             if (pid == -1) { //error
                 char *error = strerror(errno);
                 printf("error fork!!\n");
                 return 1;
    -         } else
    -             if (pid == 0) {
    -                 if(i ==0){
    +    } 
    +    if (pid == 0) { // child process
                          close(fds[1]);
                          dup2(fds[0], 0);
    -                     close(fds[0]);
    -                     execvp(argv1[0], argv1);
    +        //close(fds[0]);
    +        execvp(argv2[0], argv2); // run command AFTER pipe character in userinput
                          char *error = strerror(errno);
                          printf("unknown command\n");
                          return 0;
    -                 } else if(i == 1) { 
    +    } else { // parent process
                          close(fds[0]);
                          dup2(fds[1], 1);
    -                     close(fds[1]);
    -                     execvp(argv2[0], argv2);
    +        //close(fds[1]);
    +        execvp(argv1[0], argv1); // run command BEFORE pipe character in userinput
                          char *error = strerror(errno);
                          printf("unknown command\n");
                          return 0;
                      }
    -            } else { // parent process
    -                int childstatus;
    -                waitpid(pid, &childstatus, 0);
    -                return 1;
    -            }
    -    } // end for
     };
    
    
     int main() {    
         char cmd[MAX_CMD_LENGTH+1];    
         char * params[MAX_NUM_PARAMS+1];    
    -    char * argv1[MAX_NUM_PARAMS+1];    
    -    char * argv2[MAX_NUM_PARAMS+1];    
    +    char * argv1[MAX_NUM_PARAMS+1] = {0};    
    +    char * argv2[MAX_NUM_PARAMS+1] = {0};    
         int k, y, x;    
         int f = 1;    
         while(1) {
    @@ -95,6 +88,7 @@
                 if (strcmp(params[k], "|") == 0) {    
                     f = 0; y = k;      
                    printf("pipe found\n");
    +               break;
                 }               
             }
             if (f==0) {