简体   繁体   中英

Piping in C - Error in Command 2

So, I thought I was on the right track with trying to imitate the bash shell, but I'm having issues piping. I am getting an error executing the second command. I was wondering if someone could explain to me how to fix this and why it's going wrong. I'm very new to C & Linux commands so any supplemental information that could help me along the way would be also be appreciated.

Thank you so much for your time. My code is below, but there is a lot of it. My issue is occurring in the exec_pipe function. I would normally include what I have used for input and what I am getting for output, but my sample input is actually executable files my professor gave us for testing. Unfortunately, mine is not working like it does in the shell. I am just getting my error print out:

Inside Case 5
Inside Exec_Pipe
Error in Pipe EXECVP cmd2

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#include <time.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/wait.h>
#define BUFSIZE 1024
#define CSTRSIZE 100
#define CMDSIZE 30
#define DEBUG 1

//I referenced our blackboard source code files to create the fork functions and to deal with file descriptors
void exec_cmd(char** cmd1){
    pid_t pid;
    if((pid = fork()) < 0){
        printf("Child Process Failed\n");
    }else if(pid == 0){
        if(execvp(cmd1[0], cmd1) < 0){
            printf("Execution Failed\n"); 
            exit(1);
        }
    }else{
       wait(NULL);
    }
}
void exec_cmd_in(char** cmd1, char* infile){
    pid_t pid;
    int fdi;
    if((pid = fork()) < 0){
          printf("Child Process Failed\n");
    }else if(pid == 0){
        fdi = open(infile, O_RDONLY);
        if(fdi == -1){
            printf("No Infile");
        }
    }
}
void exec_cmd_opt_in_append(char** cmd1, char* infile, char* outfile){
   /* pid_t pid;
    int fdi, fdo;
    if((pid = fork()) < 0){
          printf("Child Process Failed\n");
    }else if(pid == 0){
        fdo = open(outfile, O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
        if(fdo == -1){
            printf("No Outfile");
        }
        if(dup2(fdi, 0) == -1){
            printf("Infile not updated");
        }
        if(dup2(fdo, 1) == -1){
            printf("Outfile not updated");
        }
        close(fdi);
        close(fdo);
        if(execvp(cmd1[0], cmd1) < 0){
            printf("Execution Failed\n"); 
            exit(1);
        }
    }else{
        wait(NULL);
    } */ 
}
void exec_cmd_opt_in_write(char** cmd1, char* infile, char* outfile){
    /* pid_t pid;
    int fdi, fdo;
    if((pid = fork()) < 0 ){
        printf("Fork Error");
        exit(1);
    }else if(pid == 0 ){
        fdo = open(outfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        if(fdo == -1){
                printf("No Outfile");
        }

        if(dup2(fdi, 0) == -1){
            printf("Infile not updated");
        }
        if(dup2(fdo, 1) == -1){
            printf("Outfile not updated");
        }
        close(fdi);
        close(fdo);
        if(execvp(cmd1[0], cmd1) < 0){
            printf("Execution Failed\n"); 
            exit(1);
        }
    }else{
        wait(NULL);
    }
  */   
}
void exec_pipe(char** cmd1, char** cmd2){
    pid_t pid;
    int pipefd[2];
    // pipe[1] is the write end of the pipe
    // pipe[0] is the read end of the pipe
    // making a pipe
    printf("Inside Exec_Pipe\n");
    pid = fork();
    switch(pid){
        case -1:
            //error in fork
            printf("Fork Error\n"); 
            //Exit
            exit(1);
        case 0:
            //child
            break;
        default:
            //parent
            wait(NULL);
    }   
    //This will be executed by child process
    if(pipe(pipefd) < 0 ) {
        //error condition
        printf("Pipe Error");
        exit(1);
    }
    pid = fork();
    switch(pid){
        case -1:
            //error in fork
            printf("Fork Error\n"); 
            //Exit
        case 0:
            //child
            close(STDIN_FILENO);
            //direct STDOUT to the pipe
            dup2(pipefd[1], STDOUT_FILENO);
            //Close descriptors
            close(pipefd[0]);
            close(pipefd[1]);
            //Execute Command1
            execvp(cmd1[0], cmd1);
            //execvp should not return, so if it does
            //there is an error!
            printf("Error in EXECVP cmd1");
            exit(1);
        default:
            //parent
            close(STDIN_FILENO);
            //direct input to the pipe
            dup2(pipefd[0],STDIN_FILENO);
            //close descriptors
            close(pipefd[0]);
            close(pipefd[1]);
            //execute command 2
            execvp(cmd2[0],cmd2);
            //if execvp makes it back, error condition
            printf("Error in Pipe EXECVP cmd2");
            exit(1);
    }   
}
void exec_pipe_opt_in_append(char** cmd1, char** cmd2, char* infile, char* outfile){

}
void exec_pipe_opt_in_write(char** cmd1, char** cmd2, char* infile, char* outfile){

}
int parse_command(char* line, char** cmd1, char** cmd2, char* infile, char* outfile){
/* 
    (1)Create a bunch of flags to compare for the right return value
    (2)Loop over the entire line and set the flags
    (3)Add a bunch of if statements to compare flags
    (4)If there is more than one flag for pipe, we can't handle it. Regurn 9.
    (5)If there is &, we can't handle.
    (6)Return the right value
*/
    int pipe_found = 0;
    int input_found = 0;
    int redirection = 0;
    int i = 0;
    int spaces = 0;
    int append = 0;
    int special = 0;
    while(line[i] != '\0'){
        if(line[i] == '|'){
            pipe_found++;
        }
        if(line[i] == '<'){
            input_found = 1;
        }
        if((line[i] == '&') || (line[i] == '*') || (line[i] == '^') || (line[i] == '%') || (line[i] == '#') || (line[i] == '!') || (line[i] == '@') || (line[i] == '(') || (line[i] == ')')){ 
            special = 1;
        }
        if(line[i] == '>'){
            redirection = 1;
            if(line[i+1] == '>'){
                append = 1;
            }
        }
        if(line[i] == ' '){
             spaces++;
        }
       i++;
    }
    if((strlen(line) >=4) && (line[0] == 'q') && (line[1] == 'u') && (line[2] == 'i') && (line[3] == 't')){
        return 0;
    }
    if((pipe_found == 0) && (special == 0)){
        if((redirection == 0) && (input_found == 0)){
            return 1;
        }else if((redirection == 0) && (input_found == 1)){
            return 2;
        }else if(append == 1){
            return 3;
        }else if(redirection == 1){
            return 4;
        }
    }else if((pipe_found == 1) && (special == 0)){
        if((redirection == 0) && (input_found == 0)){
            return 5;
        }else if((redirection == 0) && (input_found == 1)){
            return 6;
        }else if(append == 1){
            return 7;
        }else if(redirection == 1){
            return 8;
        }

    }
            return 9;
}
//I referenced StackOverflow and some online libraries to get this tokenize function
char ** tokenize(char *str, char *delim, unsigned int *number_tokens) {
    char *pch = strtok(str, delim);
    unsigned int ntok = 0;
    if(pch != NULL) {
        ntok = 1;
    }else{
        return NULL;
    }
    char **tokens = realloc(NULL, sizeof(char *)*ntok);
    tokens[ntok-1] = pch;
    while(pch != NULL) {
        pch = strtok(NULL, delim);
        ntok++;
        tokens = realloc(tokens, sizeof(char *)*ntok);
        tokens[ntok-1] = pch;
    }
    if(number_tokens) {
        *number_tokens = ntok;
    }
    return tokens;
}
//I referenced StackOverflow.com for this trim function 
char *trim(char *str) {
  char *end;
  if(str == NULL){
    return NULL;
  }
  while(isspace(*str)){
       str++;
  }
  end = str + strlen(str) - 1;
  while(end > str && isspace(*end)) {
      end--;
  }
  *(end+1) = 0;
  return str;
}

int main(int argc, char *argv[]){
    int returnValue = 0;
    char *infile = NULL;
    char *outfile = NULL;
    char **cmd = NULL;
    char **cmd1_tokens = NULL;
    char **cmd2_tokens = NULL;
    char *input;
    int current_cmd = 0;
    /*
    (1)If the user does not enter a command line argument, get one after typing "myshell-%"
    (2)Call parse_command on the user input to get the right return value
    (3)Begin parsing the user input within main
    */ 
    if(argc == 1){
        printf("myshell-%%\n"); 
        fgets (input, 20, stdin);
        returnValue = parse_command(input, cmd1_tokens, cmd2_tokens, infile, outfile);
        cmd = tokenize(input, "|", NULL);
    }else{
        returnValue = parse_command(argv[1], cmd1_tokens, cmd2_tokens, infile, outfile);
        cmd = tokenize(argv[1], "|", NULL);
    }
    int infileIt = 0;
    while(cmd[current_cmd] != NULL) {
        unsigned int number_tokens = 0;
        char **infile_token = tokenize(cmd[current_cmd], "<", &number_tokens);
        if(number_tokens > 1){
            while(infile_token[infileIt] != NULL){
                infileIt++;
            }
        }
        if(infile_token[1] != NULL) {
            number_tokens = 0;
            char **infile_outfile_token = tokenize(infile_token[1], ">", &number_tokens);
            if(number_tokens > 1){
                infile = infile_outfile_token[0];

                  infile = infile_token[1];
            }
        }
        number_tokens = 0;
        char **outfile_token = tokenize(cmd[current_cmd], ">", &number_tokens);
        if(number_tokens > 1){

                    outfile = outfile_token[1];
        }
        current_cmd++;
    }
    //Trim the in/outfiles
    infile = trim(infile);
    outfile = trim(outfile);
    /*
    Start breaking up cmd[0] and cmd[1] into smaller chunks and saving into the appropriate cmd
    */
    cmd1_tokens = tokenize(cmd[0], " ", NULL);
    if(cmd[1] != NULL){
        cmd2_tokens = tokenize(cmd[1], " ", NULL);
    }
    int cmd1Args = 0;
    while(cmd1_tokens[cmd1Args] != NULL){
        cmd1Args++;
    }
    int cmd2Args= 0;
    if(cmd2_tokens != NULL){
        while(cmd2_tokens[cmd2Args] != NULL){
            cmd2Args++;
        }
    }
    int iterator = 0;
    while((iterator < cmd1Args) && (cmd1Args != 0)){
        printf("Cmd1: %s\n", cmd1_tokens[iterator]);
        iterator++;
    }
    iterator = 0;
        while((iterator < cmd2Args)&&(cmd2Args != 0)){
        printf("Cmd2: %s\n", cmd2_tokens[iterator]);
        iterator++;
    }
    if(infile != NULL){
        printf("Infile: %s\n", infile);
    }
    if(outfile != NULL){
        printf("Outfile: %s\n", outfile);
    }

    /*Use a switch statement to process all the return values (0 ot 9) of parse_command.
    Our program should execute the “line” if the return code from parse_command
    function is 0 to 8, that is the line is deemed “valid”. For return code 9,
    our program simply output ”Not handled at this time!”.*/
    switch(returnValue){
    case 0 :
        printf("Exiting Program.\n");
        exit(1);
        break;
    case 1 :
        printf("Inside Case 1\n");
        exec_cmd(cmd1_tokens);
        break; 
    case 2 :
        printf("Inside Case 2\n");
        exec_cmd_in(cmd1_tokens, infile);
       break;
    case 3 :
        printf("Inside Case 3\n");
        exec_cmd_opt_in_append(cmd1_tokens, infile, outfile);
        break;
    case 4 :
        printf("Inside Case 4\n");
        exec_cmd_opt_in_write(cmd1_tokens, infile, outfile);
        break;
    case 5 :
        printf("Inside Case 5\n");
        exec_pipe(cmd1_tokens, cmd2_tokens);
        break;
    case 6 :
        printf("Inside Case 6\n");
        //exec_pipe_in(cmd1_tokens, cmd2_tokens, infile);
        break;
    case 7 : 
        printf("Inside Case 7\n");
        exec_pipe_opt_in_append(cmd1_tokens, cmd2_tokens, infile, outfile);
        break;
    case 8 :
        printf("Inside Case 8\n");
        exec_pipe_opt_in_write(cmd1_tokens, cmd2_tokens, infile, outfile);
        break;
    default : 
        printf("Inside Case 9\n");
        printf("Not handled at this time!\n");
}

    return 0;
}

Without having access to the input file that you're giving it, it's a little hard to say what's going on, but here are some tips for debugging it.

First, when something you don't understand is happening, it can be a good idea to strip it down to a minimal, working, self contained example that demonstrates the problem. Sometimes, just the process of cutting it down to that small example can help you to find the problem; but if not, it gives you a much smaller example to ask about.

Next, when putting in these print statements to debug what's going on, give yourself a little more context. Especially in the one that indicates an error; print out what the error is, and what the arguments were to the function that failed. Rather than just:

printf("Error in Pipe EXECVP cmd2");

You can use strerror to get a string representing the error number:

printf("Error %d in Pipe EXECVP cmd2: %s\n", errno, strerror(errno));

And you can also print out what the command and all of your arguments were:

for (char **arg = cmd2; *arg != NULL; ++arg) {
    printf("cmd2[%ld] = %s", arg - cmd2, *arg);
}

Between printing out the actual error and printing out the command name and all of the arguments, that should help you to debug the problem.

If you could add that information to your question, and maybe cut your example down to a more minimal example as well as showing a minimal example of the input that causes a problem, we could probably help out a lot more.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM