在期望脚本中通过 ssh/telnet(甚至是 shell)传输二进制数据时出错
Posted
技术标签:
【中文标题】在期望脚本中通过 ssh/telnet(甚至是 shell)传输二进制数据时出错【英文标题】:Error transporting binary data over ssh/telnet (even shell) in expect script 【发布时间】:2021-04-02 19:11:41 【问题描述】:似乎没有人再维护this repo 和this repo。
假设我想使用user/password
在expect
脚本中自动进行身份验证从远程获取数据,如下所示:
ps:我使用sh
表示 ssh/telnet 连接
$ expect -v
expect vesion 5.45.4
$ cat expect.sh
#!/usr/bin/expect -f
spawn -noecho sh -c ./download.sh
interact
$ cat download.sh
#!/bin/sh
sh -c 'cat /bin/ls' # cat a binary file
$ ./expect.sh > a.out
$ file a.out
a.out: ELF 64-bit LSB shared object, x86-64, version1 (SYSV), too many program (2304)
正如最后一条命令所说,捕获的命令ls
(即a.out)已在本地中断。而且你可以发现如果你真的运行脚本,这两个文件的字节大小是不同的(更不用说校验和了)。
虽然它可能与翻译和编码问题有关,所以我将 expect.sh
脚本更改为此,但它仍然无法正常工作
$ cat expect.sh
#!/usr/bin/expect -f
spawn -noecho sh -c ./download.sh
fconfigure $spawn_id -translation binary -encoding binary
fconfigure stdin -translation binary -encoding binary
fconfigure stdout -translation binary -encoding binary
interact
有人知道吗?
【问题讨论】:
【参考方案1】:通常将二进制文件转储到 tty 会导致意外结果。您可以在交互式 shell 中尝试cat /bin/ls
,看看会发生什么。 tty 不是直通管道。它不会准确输出发送给它的内容。
看这个简单的例子:
[STEP 101] # printf '\n'
[STEP 102] # printf '\n' | hexdump -C
00000000 0a |.|
00000001
[STEP 103] #
[STEP 104] # expect -c 'spawn -noecho printf \n; expect eof'
[STEP 105] # expect -c 'spawn -noecho printf \n; expect eof' | hexdump -C
00000000 0d 0a |..|
00000002
[STEP 106] #
[STEP 107] # expect -d -c 'spawn -noecho printf \n; expect eof'
expect version 5.45.4
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns 57875
expect: read eof
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "\r\n"
argv[0] = expect argv[1] = -d argv[2] = -c argv[3] = spawn -noecho printf \n; expect eof
set argc 0
set argv0 "expect"
set argv ""
[STEP 108] #
如您所见,\n
在输出到 tty 时会转换为 \r\n
。
【讨论】:
确实,\r\n
的翻译是按预期完成的。这对于文本文件来说并不是什么大问题,因为它不会改变太多东西。但是二进制数据的修改更加严重和烦人。
不是 Expect 将 \n
转换为 \r\n
。是 tty 执行此操作。 Expect 分配一个 pty 并在该 pty 上运行生成的进程。
是否可以在expect中禁用tty/pty分配?
不太确定。但如果您不需要 pty,则不必使用 Exepct。【参考方案2】:
如果您先将 tty 设置为原始模式,您应该会取得更大的成功。在expect
中,这是通过将全局变量stty_init
设置为您将提供stty
命令的字符串值来完成的。例如,
set stty_init raw
log_user 0
spawn ssh user@host "cat /bin/ls; sleep 1"
expect assword: send mypassword\r
expect \n
log_user 1
expect eof
将上面的标准输出直接写入一个文件,它应该只得到ls
文件的内容。请注意 log_user 0
如何用于抑制输出,直到我们读完 \n
换行符。另外,我必须在 cat 之后添加一个sleep
,否则在读取所有数据之前关闭 tty,并且文件最终比原始文件短一点。应该有更好的方法来做到这一点。
【讨论】:
我不推荐这个。它还取决于区域设置。基本上,使用 pty 传递任意二进制数据并不是一个好主意。 其实我试过set stty_init raw
也不行(以上是关于在期望脚本中通过 ssh/telnet(甚至是 shell)传输二进制数据时出错的主要内容,如果未能解决你的问题,请参考以下文章
如何在项目中通过LINK标签链接自己javascript脚本?