如何用C语言启动持续运行的Bash程序并实现双向交互
如何用C语言启动持续运行的Bash程序并实现双向交互
看起来你需要的是和一个长期运行的外部进程保持双向通信——毕竟每次用system()或者popen()都会重新启动进程,自然会丢失之前的状态。我来给你捋捋具体怎么实现:
核心思路
要实现这种持久的双向交互,我们需要手动创建两个管道:
- 输入管道:父进程写,子进程读,重定向为子进程的标准输入(用来给
L发指令) - 输出管道:子进程写,父进程读,重定向为子进程的标准输出(用来读取
L的响应)
然后通过fork()创建子进程,用exec()系列函数启动你的Bash程序L,同时在父进程里通过这两个管道持续收发数据。
完整代码示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #define BUFFER_SIZE 1024 // 封装一个发送数据并读取响应的工具函数 int send_and_receive(int write_fd, int read_fd, const char* send_msg, char* response) { // 发送数据,注意加换行(多数交互式程序是行缓冲,需要换行才会处理输入) if (write(write_fd, send_msg, strlen(send_msg)) == -1) { perror("Failed to write to pipe"); return -1; } if (write(write_fd, "\n", 1) == -1) { perror("Failed to send newline trigger"); return -1; } // 读取响应,这里简单实现单次读取,长响应可以循环读取直到EOF ssize_t bytes_read = read(read_fd, response, BUFFER_SIZE - 1); if (bytes_read == -1) { perror("Failed to read from pipe"); return -1; } response[bytes_read] = '\0'; // 确保字符串以空字符结尾 return 0; } int main() { int parent_to_child[2]; // 父写子读的输入管道 int child_to_parent[2]; // 子写父读的输出管道 pid_t pid; char buffer[BUFFER_SIZE]; // 创建两个管道,失败则直接退出 if (pipe(parent_to_child) == -1 || pipe(child_to_parent) == -1) { perror("Failed to create communication pipes"); exit(EXIT_FAILURE); } // 分叉出子进程 pid = fork(); if (pid == -1) { perror("Failed to fork child process"); exit(EXIT_FAILURE); } if (pid == 0) { // 子进程逻辑:重定向标准IO到管道,启动目标程序L // 关闭不需要的管道端(必须做,否则会导致阻塞) close(parent_to_child[1]); close(child_to_parent[0]); // 重定向子进程的标准输入到输入管道的读端 if (dup2(parent_to_child[0], STDIN_FILENO) == -1) { perror("Failed to redirect stdin"); exit(EXIT_FAILURE); } // 重定向子进程的标准输出到输出管道的写端 if (dup2(child_to_parent[1], STDOUT_FILENO) == -1) { perror("Failed to redirect stdout"); exit(EXIT_FAILURE); } // 关闭已经重定向完成的管道原文件描述符 close(parent_to_child[0]); close(child_to_parent[1]); // 启动目标程序L,如果L不在系统PATH里,要写绝对路径 execlp("L", "L", (char*)NULL); // 如果exec函数返回,说明启动失败 perror("Failed to launch program L"); exit(EXIT_FAILURE); } else { // 父进程逻辑:通过管道和子进程交互 // 关闭不需要的管道端 close(parent_to_child[0]); close(child_to_parent[1]); // 第一次交互:发送"Hello Program",获取响应 printf("Sending: Hello Program\n"); if (send_and_receive(parent_to_child[1], child_to_parent[0], "Hello Program", buffer) == 0) { printf("Received: %s", buffer); } // 第二次交互:发送"What did I send you",获取响应 printf("\nSending: What did I send you\n"); if (send_and_receive(parent_to_child[1], child_to_parent[0], "What did I send you", buffer) == 0) { printf("Received: %s", buffer); } // 发送退出指令(如果L支持exit命令),或者直接终止子进程 write(parent_to_child[1], "exit\n", 5); // 等待子进程正常结束 waitpid(pid, NULL, 0); // 关闭剩余的管道端 close(parent_to_child[1]); close(child_to_parent[0]); } return 0; }
必看注意事项
- 管道端的关闭:一定要关闭不需要的管道端!否则会导致进程在
read()或write()时无限阻塞——只有当管道的所有端都关闭时,才会触发EOF信号。 - 缓冲问题:绝大多数交互式程序(包括Bash脚本)默认是行缓冲模式,所以发送数据必须加换行符
\n,否则程序会一直等待输入结束。如果你的L是全缓冲模式,可以用stdbuf工具关闭缓冲:把execlp的启动命令改成execlp("stdbuf", "stdbuf", "-i0", "-o0", "L", (char*)NULL); - 错误处理:示例只做了基础的错误检测,实际使用时可以增强逻辑,比如通过
waitpid()监控子进程是否意外崩溃,或者处理长响应的循环读取。
内容来源于stack exchange
相关产品推荐
相关产品推荐

