0

我正在尝试使用 C99/gcc/Linux(unistd.h) 编写一个程序来从用户那里接收 Linux shell 命令,执行它们,检索它们的输出并将其存储在一个动态大小的数组/字符串中,以便输出/修改以后的日期。

在下面的代码中,我一次将一个 shell 命令传递给该函数char * cmdnode * charLinkedList所有数据,因为一些命令可以返回 5 个字符 ( pwd),而其他命令可能返回很多( cat [file])。

我已经彻底测试并确认以下内容有效:

  • 存储/检索/打印到字符链表
  • 分叉进程并让子进程执行命令(不使用管道)
  • 执行 shell 命令并将其打印到控制台(不使用管道)
  • 执行带有和不带有不同长度参数的 shell 命令

什么不起作用:

  • 将每个命令的输出通过管道传输到变量/链表
#define READPIPE 0  
#define WRITEPIPE 1
/* % Inputs %
 * char * cmd : a string containing the command
 * node * charLinkedList : a linked list of char to store the output */
void execCmd(char * cmd, node * charLinkedList) {
    int fd[2];        // fd is the pipe
    pid_t pid;        // pid is for holding the process ID

    char* shellOutput;  // shellOutput is a string to store the output in, it's
                        //> not being used in this code but I'm ok using it in place
                        //> of my character linked list (charLinkedList)

    if(pipe(fd) == -1){
        fprintf(stderr,"Pipe failed");
    }
    
    /* Logic for separating the command from it's arguments */
    int argNum = argumentCounter(cmd);   // argNum counts the arguments and returns it
    char * args[argNum + 2];            // for holding the arguments that we will be passing later on
    char ** delimArguments;             // to hold the deliminated command string

    if(argNum > 0) {                                //if there are arguments with the command
        delimArguments = stringSplitter(cmd, ' ');  //stringSplitter splits the string at a deliminer and returns it
        for (int i = 0; i < (argNum + 1); i++){     //loads the commands into an array
            args[i] = *(delimArguments + i);        // this logic has been tested and works
        }
        args[argNum + 1] = 0;                       // please ignore this spaghetti code, I'll clean
    }                                               //> it up later I promise
    
    /* Forking & executing the commands */
    pid = fork();
    if(pid == -1) {
        printf("FORK FAILED\n");
    }

    if (pid == 0) { /* Child Process */
        
        close(fd[READPIPE]); // same as close(fd[0])
        
        /* This next if/else statement is for toggling between whether or not the
         * command has arguments or not. It's a mess and I'll 100% be merging 
         * this at a later date. */

        if (argNum > 0){
            /* command with arguments */ //i.e. ls -l -a
            dup2(fd[WRITEPIPE], STDOUT_FILENO);  //duplicate the STDOUT to the write end of the pipe (fd[1])
            execvp(args[0], args);               //execute the command and it's arguments
        } else {
            /* command without argument */ //i.e. ls
            dup2(fd[WRITEPIPE], STDOUT_FILENO); //duplicate the STDOUT to the write end of the pipe (fd[1])
            execlp(cmd, cmd, NULL);             //execute the command
        }
        close(fd[WRITEPIPE]);  //if something goes wrong and the code gets to here, close the pipe
        return;      //return to int main() function; don't continue this one as the child
    } else { /* Parent Process */
        close(fd[WRITEPIPE]);       //closing the writing side of the pipe
        fcntl(fd[READPIPE], F_SETFL, fcntl(fd[READPIPE], F_GETFL) | O_NONBLOCK); //using fcntl to get the file descriptor of the pipe

        FILE * theOutput;                          // creating a file to point at the output
        theOutput = fdopen(fd[READPIPE], "r");     // opening the stream from the child process
        char buffer;                               // a char for grabbing output one character at a time
        while((buffer = fgetc(theOutput)) != NULL) // while we havn't hit the end of the file
        {
            addToQueue(charLinkedList, buffer);    // one by one add each character of the output to
        }                                          //> the linked list queue
        printLinkedList(charLinkedList);           //outputting the list to verify the output captured
    }

我尝试使用read()write()功能,虽然我无法让它工作。我实现了dup2以克隆STDOUT到管道,然后从那里与之斗争,试图让它工作(没有成功)。我发现另一篇文章建议按照我目前的方式做事,尽管我不知道如何从文件描述符中获取输出。

我已经尝试使用两者fgetcfgets无济于事,我有点卡在这一点上。这是正确的方法还是有更好的方法将子进程的输出转换为父进程中execvp的字符串/链表/变量?

4

0 回答 0