如何用C语言与Shell交互?含SSH带密码执行命令需求
C语言实现带密码的SSH命令执行方法
你遇到的问题本质是ssh的密码输入是直接和终端(TTY)交互,而非通过标准输入流,所以popen、普通管道这类仅处理标准IO的方法无法捕获密码提示符并输入密码。下面是两种可靠的解决方案:
方法1:使用libssh(纯C SSH协议库)
这是最推荐的方案,直接通过SSH协议和目标主机通信,不需要依赖shell或模拟终端,安全性和可控性更高。
步骤示例:
- 先安装libssh开发包(比如
apt install libssh-dev或yum install libssh-devel) - 编写C代码实现连接、认证、执行命令:
#include <libssh/libssh.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { ssh_session my_ssh_session; int rc; char *password = "your_password"; // 注意:实际使用中不要硬编码,建议从安全渠道读取 char *command = "ls"; char buffer[256]; int nbytes; // 初始化SSH会话 my_ssh_session = ssh_new(); if (my_ssh_session == NULL) exit(-1); // 设置连接参数 ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "192.168.1.1"); ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, "root"); // 连接到服务器 rc = ssh_connect(my_ssh_session); if (rc != SSH_OK) { fprintf(stderr, "连接失败: %s\n", ssh_get_error(my_ssh_session)); ssh_free(my_ssh_session); exit(-1); } // 密码认证 rc = ssh_userauth_password(my_ssh_session, NULL, password); if (rc != SSH_OK) { fprintf(stderr, "认证失败: %s\n", ssh_get_error(my_ssh_session)); ssh_disconnect(my_ssh_session); ssh_free(my_ssh_session); exit(-1); } // 执行命令 ssh_channel channel = ssh_channel_new(my_ssh_session); if (channel == NULL) exit(-1); rc = ssh_channel_open_session(channel); if (rc != SSH_OK) { ssh_channel_free(channel); exit(-1); } rc = ssh_channel_request_exec(channel, command); if (rc != SSH_OK) { ssh_channel_close(channel); ssh_channel_free(channel); exit(-1); } // 读取命令输出 nbytes = ssh_channel_read(channel, buffer, sizeof(buffer)-1, 0); while (nbytes > 0) { buffer[nbytes] = '\0'; printf("%s", buffer); nbytes = ssh_channel_read(channel, buffer, sizeof(buffer)-1, 0); } // 清理资源 ssh_channel_send_eof(channel); ssh_channel_close(channel); ssh_channel_free(channel); ssh_disconnect(my_ssh_session); ssh_free(my_ssh_session); return 0; }
编译命令:gcc ssh_test.c -o ssh_test -lssh
方法2:使用libexpect(模拟终端交互)
如果你更倾向于通过调用ssh命令的方式实现,可以用libexpect库,它能模拟终端环境,捕获ssh的密码提示符并自动输入密码。
步骤示例:
- 安装libexpect开发包(比如
apt install libexpect-dev) - 编写C代码:
#include <expect.h> #include <stdio.h> int main() { expect_t *exp; char *prompt = "password:"; // ssh的密码提示符,可能因系统不同略有差异 char *password = "your_password"; char *command = "ssh root@192.168.1.1 ls"; char buffer[1024]; int status; // 初始化expect会话 exp = exp_spawnl(command, command, NULL); if (exp == NULL) { perror("spawn failed"); return -1; } // 捕获密码提示符并发送密码 exp_expectl(exp, exp_glob, prompt, exp_send, "%s\n", password, exp_end); // 读取命令输出 while (exp_read(exp, buffer, sizeof(buffer)-1) > 0) { buffer[strcspn(buffer, "\r")] = '\0'; // 处理换行符 printf("%s\n", buffer); } // 等待命令执行完成并获取退出状态 exp_wait(exp, &status, 0); exp_close(exp); return WEXITSTATUS(status); }
编译命令:gcc expect_ssh.c -o expect_ssh -lexpect -lutil
不推荐的临时方案:调用sshpass
如果你只是快速实现,也可以通过popen调用sshpass工具(需要先安装),但这种方式密码可能会暴露在进程列表中,安全性极低,仅适合测试环境:
#include <stdio.h> #include <stdlib.h> int main() { char cmd[256]; FILE *fp; char buffer[1024]; snprintf(cmd, sizeof(cmd), "sshpass -p 'your_password' ssh root@192.168.1.1 ls"); fp = popen(cmd, "r"); if (fp == NULL) { perror("popen failed"); return -1; } while (fgets(buffer, sizeof(buffer), fp) != NULL) { printf("%s", buffer); } pclose(fp); return 0; }
注意事项
- 硬编码密码存在严重安全风险,实际应用中建议从环境变量、加密配置文件或安全密钥管理系统读取密码。
- 优先使用libssh这类原生SSH库,避免依赖外部工具或shell,提升程序的稳定性和安全性。
内容的提问来源于stack exchange,提问作者dustChou
相关产品推荐
相关产品推荐

