Linux下能否请求运行中Shell执行命令并获输出?C与Bash实现可行吗?
如何让已运行的Shell进程执行命令并获取输出
当然可以实现!不过首先得明确:exec*和system这类函数都是启动新的Shell进程,而新进程完全没有原Shell的状态(比如目录栈dirs、后台任务jobs这些都是Shell进程内部的变量/状态),所以要获取目标Shell的特定状态,必须让正在运行的那个Shell实例来执行命令,这得靠进程间通信(IPC)来实现。
下面分Bash脚本和C语言两种场景来讲解:
一、用Bash实现的简单方案(基于命名管道FIFO)
这是最直接的方式,让目标Shell监听一个管道,接收外部发来的命令并执行,再把输出发回来。
步骤1:在目标Shell中设置IPC通道
先在你想要控制的那个bash里执行以下代码,创建两个命名管道并启动一个后台循环来处理命令:
# 创建命令输入和输出的命名管道 mkfifo /tmp/cmd_pipe /tmp/output_pipe # 后台循环:读取命令管道的内容,执行后把输出写入输出管道 while true; do # 读取命令(如果管道没数据会阻塞等待) if read cmd < /tmp/cmd_pipe; then # 执行命令,把标准输出和错误都重定向到输出管道 eval "$cmd" > /tmp/output_pipe 2>&1 fi done & # 记录后台循环的PID,方便后续关闭 echo $! > /tmp/pipe_loop.pid
步骤2:从外部发送命令并获取输出
在另一个终端或者脚本里,你可以这样发送命令并读取结果:
# 发送命令到目标Shell echo "dirs -v" > /tmp/cmd_pipe # 读取命令的输出 cat /tmp/output_pipe
注意事项:
eval会执行任何传入的命令,所以如果是不可信的命令,会有安全风险,使用时要谨慎。- 当不再需要这个通道时,记得清理:
kill $(cat /tmp/pipe_loop.pid); rm /tmp/cmd_pipe /tmp/output_pipe /tmp/pipe_loop.pid
二、用C语言实现的方案
原理和Bash版本一致,就是通过FIFO或者Unix域套接字和目标Shell通信。这里以FIFO为例:
1. 目标Shell的准备工作
和上面Bash的步骤1一样,先在目标Shell里创建FIFO并启动处理循环。
2. C程序代码示例
这个程序负责发送命令到FIFO,并读取输出:
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #define CMD_PIPE "/tmp/cmd_pipe" #define OUTPUT_PIPE "/tmp/output_pipe" int main() { int cmd_fd, output_fd; char cmd[] = "jobs\n"; // 要执行的命令 char buffer[1024]; ssize_t bytes_read; // 打开命令管道,写入命令 cmd_fd = open(CMD_PIPE, O_WRONLY); if (cmd_fd == -1) { perror("Failed to open command pipe"); exit(EXIT_FAILURE); } write(cmd_fd, cmd, strlen(cmd)); close(cmd_fd); // 打开输出管道,读取结果 output_fd = open(OUTPUT_PIPE, O_RDONLY); if (output_fd == -1) { perror("Failed to open output pipe"); exit(EXIT_FAILURE); } while ((bytes_read = read(output_fd, buffer, sizeof(buffer)-1)) > 0) { buffer[bytes_read] = '\0'; printf("Command output:\n%s", buffer); } close(output_fd); return 0; }
编译运行:
gcc -o shell_cmd shell_cmd.c ./shell_cmd
三、其他可选方案
如果目标Shell运行在screen或tmux会话中,也可以用它们的内置命令发送指令:
- 比如用
screen:# 发送命令到名为"my_session"的screen会话 screen -S my_session -X stuff "dirs -v > /tmp/screen_out\n" # 读取输出 cat /tmp/screen_out tmux类似:tmux send-keys -t my_session "dirs -v > /tmp/tmux_out" C-m cat /tmp/tmux_out
为什么不能用exec/system?
再强调一下:exec*函数会替换当前进程为新的Shell,system则是fork一个新进程来运行Shell,这两个新Shell都是独立的,没有原Shell的目录栈、后台任务等状态,所以执行dirs或jobs只能得到空结果或者新Shell的状态,完全不是你想要的目标Shell的信息。
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

