如何获取system()执行SFTP脚本的退出码并捕获Permission denied错误
解决SFTP Expect脚本错误捕获与C++程序错误检测问题
你的现有脚本在处理Permission denied这类错误时不够严谨,而且C++端也没有正确捕获错误状态和信息。下面我会一步步帮你修改脚本和代码,实现可靠的错误捕获:
第一步:改进Expect脚本的错误处理逻辑
原脚本在输入密码后没有处理权限拒绝的情况,只会持续等待sftp>提示符最终超时退出,且没有返回明确的错误码。我们需要修改脚本,让它能识别并处理常见错误,同时返回可被C++程序识别的退出状态:
#!/usr/bin/expect -f # 设置超时时间,避免无限等待 set timeout 10 spawn sftp -P <port number> <user name>@<IP> # 捕获密码提示,处理连接超时情况 expect { timeout { send_user "Error: Connection timeout or password prompt not found\n" exit 1 } "<user name>@<IP>'s password:" } send "<password>\n" # 处理密码输入后的结果:成功进入sftp或权限拒绝 expect { timeout { send_user "Error: Timeout after entering password\n" exit 1 } "Permission denied" { send_user "Error: Permission denied (wrong password or user permissions)\n" exit 2 } "sftp>" } # 处理远程目录切换错误 send "cd <source directory>\n" expect { timeout { send_user "Error: Timeout during cd to source directory\n" exit 3 } "No such file or directory" { send_user "Error: Source directory does not exist\n" exit 3 } "sftp>" } # 处理本地目录切换错误 send "lcd <destination directory>\n" expect { timeout { send_user "Error: Timeout during lcd to destination directory\n" exit 4 } "No such file or directory" { send_user "Error: Destination directory does not exist\n" exit 4 } "sftp>" } # 处理文件下载错误 send "get <file name>\n" expect { timeout { send_user "Error: Timeout during file download\n" exit 5 } "No such file or directory" { send_user "Error: Target file not found on server\n" exit 5 } "sftp>" } send "exit\n" expect eof # 下载成功时返回0 exit 0
脚本修改说明:
- 给每个
expect块都添加了错误/超时处理,遇到问题立刻退出并返回唯一的错误码(1-5对应不同错误场景) - 明确捕获
Permission denied错误,触发时返回错误码2,并输出清晰的错误提示 - 用
expect eof替代原有的expect -exact "$",更可靠地等待SFTP会话正常结束 - 成功完成所有操作后返回0,方便C++端快速判断执行结果
第二步:修改C++代码,捕获错误状态和信息
原有的system()调用只能获取脚本的退出码,但无法直接拿到脚本输出的错误信息。我们可以把脚本的输出(标准输出和标准错误)重定向到临时文件,然后读取文件内容,同时通过system()的返回值判断是否出错:
#include <fstream> #include <string> #include <cstdlib> #include <iostream> #include <sys/wait.h> void downloadFileFromServer() { std::ofstream downloadScript; std::string command; const std::string scriptPath = "/temp/download.sh"; const std::string outputLog = "/temp/download_log.txt"; // 创建并写入改进后的expect脚本 downloadScript.open(scriptPath); if (!downloadScript.is_open()) { std::cerr << "Failed to create download script!" << std::endl; return; } downloadScript << "#!/usr/bin/expect -f\n"; downloadScript << "set timeout 10\n"; downloadScript << "spawn sftp -P " << "<port number>" << " " << "<user name>" << "@" << "<IP>" << "\n"; downloadScript << "expect {\n"; downloadScript << " timeout {\n"; downloadScript << " send_user \"Error: Connection timeout or password prompt not found\\n\"\n"; downloadScript << " exit 1\n"; downloadScript << " }\n"; downloadScript << " \"" << "<user name>" << "@" << "<IP>" << "'s password:\"\n"; downloadScript << "}\n"; downloadScript << "send \"" << "<password>" << "\\n\"\n"; downloadScript << "expect {\n"; downloadScript << " timeout {\n"; downloadScript << " send_user \"Error: Timeout after entering password\\n\"\n"; downloadScript << " exit 1\n"; downloadScript << " }\n"; downloadScript << " \"Permission denied\" {\n"; downloadScript << " send_user \"Error: Permission denied (wrong password or user)\\n\"\n"; downloadScript << " exit 2\n"; downloadScript << " }\n"; downloadScript << " \"sftp>\"\n"; downloadScript << "}\n"; downloadScript << "send \"cd " << "<source directory>" << "\\n\"\n"; downloadScript << "expect {\n"; downloadScript << " timeout {\n"; downloadScript << " send_user \"Error: Timeout during cd to source directory\\n\"\n"; downloadScript << " exit 3\n"; downloadScript << " }\n"; downloadScript << " \"No such file or directory\" {\n"; downloadScript << " send_user \"Error: Source directory does not exist\\n\"\n"; downloadScript << " exit 3\n"; downloadScript << " }\n"; downloadScript << " \"sftp>\"\n"; downloadScript << "}\n"; downloadScript << "send \"lcd " << "<destination directory>" << "\\n\"\n"; downloadScript << "expect {\n"; downloadScript << " timeout {\n"; downloadScript << " send_user \"Error: Timeout during lcd to destination directory\\n\"\n"; downloadScript << " exit 4\n"; downloadScript << " }\n"; downloadScript << " \"No such file or directory\" {\n"; downloadScript << " send_user \"Error: Destination directory does not exist\\n\"\n"; downloadScript << " exit 4\n"; downloadScript << " }\n"; downloadScript << " \"sftp>\"\n"; downloadScript << "}\n"; downloadScript << "send \"get " << "<file name>" << "\\n\"\n"; downloadScript << "expect {\n"; downloadScript << " timeout {\n"; downloadScript << " send_user \"Error: Timeout during file download\\n\"\n"; downloadScript << " exit 5\n"; downloadScript << " }\n"; downloadScript << " \"No such file or directory\" {\n"; downloadScript << " send_user \"Error: File not found on server\\n\"\n"; downloadScript << " exit 5\n"; downloadScript << " }\n"; downloadScript << " \"sftp>\"\n"; downloadScript << "}\n"; downloadScript << "send \"exit\\n\"\n"; downloadScript << "expect eof\n"; downloadScript << "exit 0\n"; downloadScript.close(); // 设置脚本权限并运行,同时重定向输出到日志文件 command = "chmod +x " + scriptPath + " && " + scriptPath + " > " + outputLog + " 2>&1"; int exitCode = system(command.c_str()); // 解析脚本执行结果 if (WIFEXITED(exitCode)) { int scriptExitCode = WEXITSTATUS(exitCode); if (scriptExitCode == 0) { std::cout << "File downloaded successfully!" << std::endl; } else { std::cerr << "Download failed with exit code: " << scriptExitCode << std::endl; // 读取日志文件获取详细错误信息 std::ifstream logFile(outputLog); if (logFile.is_open()) { std::string line; std::cerr << "Error details:\n"; while (std::getline(logFile, line)) { std::cerr << line << std::endl; } logFile.close(); } // 根据退出码做针对性提示 if (scriptExitCode == 2) { std::cerr << "* Specific error: Permission denied (check password or user permissions on server)" << std::endl; } } } else { std::cerr << "Script execution was interrupted unexpectedly!" << std::endl; } // 可选:清理临时文件 remove(scriptPath.c_str()); remove(outputLog.c_str()); }
C++代码修改说明:
- 添加了日志文件重定向,把脚本的所有输出(包括错误信息)保存到临时日志文件中
- 使用
WIFEXITED和WEXITSTATUS解析system()的返回值,获取脚本的实际退出码 - 根据退出码判断错误类型,比如退出码2对应
Permission denied错误 - 读取日志文件,把详细错误信息输出给用户,方便调试
- 最后清理临时脚本和日志文件(可根据需求保留日志用于排查问题)
这样修改后,你的程序就能准确捕获Permission denied错误,以及其他可能出现的SFTP操作错误,同时能拿到清晰的错误信息用于问题定位。
内容的提问来源于stack exchange,提问作者user123456
相关产品推荐
相关产品推荐

