帮我用expect通过ssh驱动Zmodem

问题描述 投票:0回答:3

有一个漂亮的小工具叫做 zssh 这使得使用起来很容易

lszrz
使用传输文件的实用程序 z调制解调器 通过现有的 ssh 联系。 出奇的方便……但看来我应该 能够使用
expect
完成同样的事情。 我已经得到了 到目前为止...

#!/usr/bin/expect -f

spawn ssh $argv
set ssh_spawn_id $spawn_id
send_user "ssh is: $ssh_spawn_id\n"

interact -o "\030B0000" {
    send_user "\nStarting zmodem receive.\n"

    spawn rz -v
    set rz_spawn_id $spawn_id
    send_user "rz is: $rz_spawn_id\n"

    while {1} {
        expect {
            eof break

            -i $rz_spawn_id -re .+ {
                send -raw -i $ssh_spawn_id $expect_out(buffer)
            }
            -i $ssh_spawn_id -re .+ {
                send -raw -i $rz_spawn_id $expect_out(buffer)
            }
        }
    }

    send_user "\nFinished zmodem receive.\n"
    set spawn_id $ssh_spawn_id
}

看到

rz
框架后启动
ZRQINIT
,并且它显然连接
rz
到ssh会话,但是不起作用。
rz
说:

Retry 0: Bad CRCe.**B0100000023be50
Retry 0: Bad CRC**B0600000023d984
Retry 0: Bad CRC**B0600000023d984

...等等。

有办法让这个工作成功吗? 谢谢!

scripting ssh interactive expect zmodem
3个回答
4
投票

我发现在发送方使用

-e
/
--escape
(转义所有控制字符)选项有助于解决启动 zmodem 连接时出现的一些问题。

例如:

发送文件:

sz -e somefile.ext

接收文件:

rz -e

这对于通过 IPMI sol(局域网串行)链路传输文件非常方便。

OSX 上有 iterm2-zmodem,Linux 上有 Konsole,集成了 Zmodem。


2
投票
  1. 在调试时使用

    exp_internal 1
    非常有用。您可以查看 Expect 与传入文本的匹配情况。

  2. 我想知道终端是否妨碍了。在生成

    rz
    之前,尝试
    stty raw
    。然后在
    send_user "Finished..."
    之后做
    stty -raw

  3. 您可以使用

    exp_continue
    代替
    while
    循环:

    spawn rz -v
    set rz_spawn_id $spawn_id
    send_user "rz is: $rz_spawn_id\n"
    
    expect {
        -i $rz_spawn_id -re .+ {
            send -raw -i $ssh_spawn_id $expect_out(buffer)
            exp_continue
        }
        -i $ssh_spawn_id -re .+ {
            send -raw -i $rz_spawn_id $expect_out(buffer)
            exp_continue
        }
        eof
    }
    

    这与问题无关,只是风格问题。


0
投票

感谢您提出这个问题,它帮助我开始自己成功地做到这一点。

我想要类似的东西,这样我就可以通过 QEMU 虚拟串行控制台套接字(与 Proxmox

qm term $vmid
命令相同)使用 Proxmox QEMU VM 的终端发送和接收文件(分时,而不是多个流)用途)。

自从我编写下面的脚本以来已经有一段时间了,它对于这个答案来说绝对是必要的,并且仍然存在问题,但是您的代码被破坏的主要原因是因为需要配置 TCL 通道二进制模式与

chan configure $channel_id -translation binary
.

从“本地”机器发送是通过在空终端中键入

szsend
来触发的(其中expect然后将键入接收命令),然后它将调出fzf进行文件选择,并且从远程机器发送是通过以下方式完成的运行 sz,并期望将检测传输标头并在本地启动 rz。

#!/usr/bin/env -S expect --
#exp_internal 1
#strace 1

#TODO disable error correction in lrzsz since we are on a reliable connection

proc spawn_channel {command} {
  puts [concat < spawn $command >]
  spawn -noecho -open [open $command wb+]
  remove_nulls 0
  set channel_id $spawn_id
  chan configure $channel_id -translation binary
  return $channel_id
}

proc local_file_select {} {
  set fzf_id [spawn -noecho fzf --print0 --expect=enter]

  interact {
    "exit" {
      send_user "\n"
      exit
    }
    #TODO "unreliable"?
    -o -re "enter\0(.*)\0" {
      set selected_file "$interact_out(1,string)"
      #send_user "You selected: $interact_out(1,string)\n"
    }
  }

  return $selected_file
}
# Can this be simplified to some pipe?
proc handle_zmodem {sz_id rz_id} {
  #TODO maxtogggle
  log_user 0
  expect {
    -i $rz_id -re "(\x00|.)+" {
      send -raw -i $sz_id -- "$expect_out(buffer)"
      exp_continue
    }
    -i $sz_id -re "(\x00|.)+" {
      send -raw -i $rz_id -- "$expect_out(buffer)"
      exp_continue
    }
    # TODO does eof even need to be handled explicitly
    eof
  }
  log_user 1
}

proc filetransfer_interact {spid} {
  set spawn_id $spid
  # note order on the -o matters
  interact {
    exit exit
    #TODO -re seems to be the way to not immediately forward to the user process
    "szsend" {
      set rz_id $spid
      set sz_id [spawn_channel [concat "|sz -vv -E" [local_file_select] "2>szerr"]]
      #TODO only works if terminal prompt
      #TODO
      #TODO rzcommand / check os; lrz waiting to receive.
      send "lrz -vv 2>lrzerr\n"
      handle_zmodem $sz_id $spid
    }
    -o "rz\015**\030B0000" {
      # TODO why does this only work if its in here?
      set rz_id [spawn_channel "|rz -vv 2>rzerr"]
      handle_zmodem $spid $rz_id
      #TODO if handle is successful or whatever?
      #return
    }
  }
}

proc proc_args {} {
  set ::vmid [lindex $::argv 0]
  set ::argv [lrange $::argv 1 end]
}

proc attach_serial {vmid} {
  set socket_path [exec sh -c [concat qm showcmd $vmid | grep -oP {'id=serial[0-9]+,path=\K[^,]+'} ]]
  set spawn_id [spawn_channel "|socat - UNIX-CONNECT:$socket_path"]
  puts "socat is $spawn_id"
  return $spawn_id
}

proc main {} {
  proc_args
  # Note interact implicitly sets raw per the docs though
  set stty_init raw
  filetransfer_interact [attach_serial $::vmid]
}

main
© www.soinside.com 2019 - 2024. All rights reserved.