在期望脚本中通过 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/passwordexpect 脚本中自动进行身份验证从远程获取数据,如下所示: 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)传输二进制数据时出错的主要内容,如果未能解决你的问题,请参考以下文章

在脚本中通过ssh捕获bash脚本的输出

如何在项目中通过LINK标签链接自己javascript脚本?

如何在调用脚本中通过正则表达式执行 sql 脚本?

如何在 Rails 中通过 jQuery 调用 JSON 文件?

在 Bash 脚本中通过管道传入/传出剪贴板

在 Bash 脚本中通过管道传入/传出剪贴板