我正在尝试使用 C99/gcc/Linux(unistd.h) 编写一个程序来从用户那里接收 Linux shell 命令,执行它们,检索它们的输出并将其存储在一个动态大小的数组/字符串中,以便输出/修改以后的日期。
在下面的代码中,我一次将一个 shell 命令传递给该函数char * cmd
(node * 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
到管道,然后从那里与之斗争,试图让它工作(没有成功)。我发现另一篇文章建议按照我目前的方式做事,尽管我不知道如何从文件描述符中获取输出。
我已经尝试使用两者fgetc
但fgets
无济于事,我有点卡在这一点上。这是正确的方法还是有更好的方法将子进程的输出转换为父进程中execvp
的字符串/链表/变量?