You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 18:19:08