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

Expect脚本遍历SFTP文件列表并本地处理的执行异常问题

问题:Expect脚本处理SFTP文件时本地操作未执行/循环不等待完成

需求是连接SFTP服务器,遍历文件列表完成:1. 从SFTP获取文件;2. 在本地执行文件操作,再处理下一个文件。但现有脚本中本地操作从未执行,或循环未等待操作完成。

原脚本代码:

set filelist_path filelist.txt

proc slurp {file} {
    set fh [open $file r]
    set ret [read $fh]
    close $fh
    return $ret
}

set filelist [slurp $filelist_path]
set rundir [file normalize [file dirname .]]

### Start connection to ftp server
spawn sftp ${user}@${host}
expect "Password:"
send "$pass\r"

### Download and manipulate files in the list


foreach item $filelist {
    expect "sftp>"
    send_user "Processing $item : \n"
    set filedir [file dirname ${item}]
    set outdir ${rundir}/tmp${filedir}
    puts [exec mkdir -p $outdir]
    send "lcd $outdir \r"
    send "get -p /data'${item}'\r"
    send "lcd $rundir \r"
    puts [exec <some cmd>]
    puts [exec rm -r ./tmp]
}

send "quit\r"
expect eof

问题原因及修复方案

1. 文件列表读取错误

slurp函数读取整个文件为单个字符串,foreach item $filelist会遍历每个字符而非每行文件路径。需要按行分割并过滤空行:

set filelist [split [slurp $filelist_path] \n]
# 过滤空行
set filelist [lsearch -all -inline -not -exact $filelist ""]

2. 未等待SFTP命令执行完成

执行send发送SFTP命令后,必须等待sftp>提示符返回,确认命令执行完毕,再执行本地操作。比如lcd和get命令后都要等待提示符:

send "lcd $outdir\r"
expect "sftp>" ;# 等待切换目录完成

send "get -p /data${item}\r"
expect "sftp>" ;# 等待文件下载完成

send "lcd $rundir\r"
expect "sftp>" ;# 等待切回原目录完成

注意:原代码中/data'${item}'的单引号可能导致路径错误,直接拼接/data${item}即可(如果文件名含特殊字符,需要转义,但先保证路径正确)

3. 本地操作的时机与错误处理

  • 本地操作(<some cmd>)必须在SFTP下载完成后执行,且要确保exec命令不会因错误中断脚本。可以用catch捕获错误:
if {[catch {exec <some cmd>} result]} {
    send_user "本地命令执行失败: $result\n"
} else {
    send_user "本地命令执行完成: $result\n"
}
  • 循环内每次删除./tmp会导致后续文件处理时目录不存在,应该删除当前文件对应的$outdir,或者在所有文件处理完成后统一删除整个tmp目录。

修正后的完整脚本

set filelist_path filelist.txt

proc slurp {file} {
    set fh [open $file r]
    set ret [read $fh]
    close $fh
    return $ret
}

# 读取并处理文件列表:按行分割+过滤空行
set filelist [split [slurp $filelist_path] \n]
set filelist [lsearch -all -inline -not -exact $filelist ""]
set rundir [file normalize [file dirname [info script]]] ;# 用info script获取当前脚本目录更可靠

### Start connection to SFTP server
spawn sftp ${user}@${host}
expect "Password:"
send "$pass\r"
expect "sftp>" ;# 等待登录成功进入SFTP交互

### Download and manipulate files in the list
foreach item $filelist {
    send_user "Processing $item : \n"
    set filedir [file dirname ${item}]
    set outdir ${rundir}/tmp${filedir}

    # 创建本地目录
    if {[catch {exec mkdir -p $outdir} mkdir_result]} {
        send_user "创建目录失败: $mkdir_result\n"
        continue ;# 跳过当前文件处理
    }

    # 切换SFTP本地工作目录
    send "lcd $outdir\r"
    expect "sftp>"

    # 下载文件
    send "get -p /data${item}\r"
    expect {
        "sftp>" {
            send_user "$item 下载完成\n"
        }
        "No such file or directory" {
            send_user "$item 不存在,跳过\n"
            send "lcd $rundir\r"
            expect "sftp>"
            continue
        }
    }

    # 切回原工作目录
    send "lcd $rundir\r"
    expect "sftp>"

    # 执行本地操作
    if {[catch {exec <some cmd> -- $outdir/[file tail $item]} cmd_result]} {
        send_user "本地操作失败: $cmd_result\n"
    } else {
        send_user "本地操作完成: $cmd_result\n"
    }

    # 删除当前文件的临时目录
    if {[catch {exec rm -r $outdir} rm_result]} {
        send_user "删除临时目录失败: $rm_result\n"
    }
}

send "quit\r"
expect eof

额外注意事项

  • 用[info script]获取当前脚本所在目录,比file dirname .更可靠,避免因执行脚本的工作目录不同导致路径错误。
  • 增加了SFTP命令的异常捕获(比如文件不存在的情况),让脚本更健壮。
  • 本地命令中最好明确指定处理的文件路径,避免依赖工作目录。

内容的提问来源于stack exchange,提问作者TimoT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:52:46