expect-pty 0.5.3 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -1
- data/README.md +122 -130
- data/docs/API.md +113 -0
- data/docs/MIGRATION.md +68 -0
- data/lib/expect/cleanup.rb +34 -0
- data/lib/expect/interaction.rb +228 -210
- data/lib/expect/logging.rb +117 -162
- data/lib/expect/matcher.rb +42 -38
- data/lib/expect/pattern.rb +10 -3
- data/lib/expect/pattern_list.rb +39 -9
- data/lib/expect/redactor.rb +12 -5
- data/lib/expect/relay.rb +106 -81
- data/lib/expect/relay_writer.rb +7 -5
- data/lib/expect/result.rb +33 -9
- data/lib/expect/session.rb +554 -0
- data/lib/expect/session_resources.rb +19 -18
- data/lib/expect/version.rb +2 -2
- data/lib/expect.rb +68 -578
- data/sig/expect.rbs +279 -0
- metadata +14 -123
- data/.rubocop.yml +0 -66
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -28
- data/Rakefile +0 -38
- data/benchmark/matching.rb +0 -98
- data/benchmark/redactor.rb +0 -64
- data/benchmark/relay.rb +0 -48
- data/benchmark/scaling.rb +0 -70
- data/benchmark/send_slow.rb +0 -41
- data/benchmark/support.rb +0 -114
- data/docs/COMPATIBILITY.md +0 -98
- data/docs/INTERNAL_CONTRACTS.md +0 -154
- data/docs/PERFORMANCE.md +0 -165
- data/docs/RELEASING.md +0 -84
- data/docs/VERIFICATION.md +0 -456
- data/examples/dialogue.rb +0 -30
- data/examples/kibitz/README.md +0 -81
- data/examples/kibitz/kibitz.rb +0 -142
- data/examples/kibitz/test_kibitz.rb +0 -37
- data/examples/ssh_auto.rb +0 -94
- data/examples/ssh_interact.rb +0 -159
- data/examples/ssh_login.rb +0 -64
- data/expect-pty.gemspec +0 -33
- data/lib/expect/configuration.rb +0 -122
- data/lib/expect/terminal.rb +0 -102
- data/script/ci +0 -121
- data/script/release.rb +0 -319
- data/test/buffer_accounting_test.rb +0 -83
- data/test/cleanup_test.rb +0 -251
- data/test/compare_upstream.rb +0 -157
- data/test/configuration_test.rb +0 -133
- data/test/deadline_test.rb +0 -237
- data/test/diagnostics_test.rb +0 -353
- data/test/edge_case_test.rb +0 -262
- data/test/fixtures/ssh_scripts/01_identity.sh +0 -4
- data/test/fixtures/ssh_scripts/02_output.sh +0 -5
- data/test/fixtures/ssh_scripts/03_delayed.sh +0 -6
- data/test/fixtures/ssh_scripts/04_failure.sh +0 -2
- data/test/fixtures/ssh_scripts/05_recovery.sh +0 -3
- data/test/initialization_failure_test.rb +0 -92
- data/test/integration/README.md +0 -109
- data/test/integration/ssh_scripts.rb +0 -94
- data/test/interact_test.rb +0 -253
- data/test/interconnect_test.rb +0 -425
- data/test/io_test.rb +0 -321
- data/test/kibitz_test.rb +0 -45
- data/test/lifecycle_contract_test.rb +0 -71
- data/test/literal_scan_test.rb +0 -97
- data/test/matching_test.rb +0 -211
- data/test/multi_session_test.rb +0 -66
- data/test/ownership_sequence_test.rb +0 -208
- data/test/pattern_offset_test.rb +0 -43
- data/test/process_interruption_test.rb +0 -296
- data/test/process_test.rb +0 -291
- data/test/redactor_test.rb +0 -183
- data/test/relay_recovery_test.rb +0 -451
- data/test/relay_reentrancy_test.rb +0 -159
- data/test/release_test.rb +0 -375
- data/test/ruby_api_test.rb +0 -515
- data/test/scan_reuse_test.rb +0 -109
- data/test/script_logging_test.rb +0 -124
- data/test/support/interact_probe.rb +0 -125
- data/test/support/kibitz_probe.rb +0 -177
- data/test/support/script_probe.rb +0 -158
- data/test/terminal_cleanup_test.rb +0 -349
- data/test/test_helper.rb +0 -58
- data/test/timeout_test.rb +0 -289
- data/test/write_contract_test.rb +0 -105
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93b7554db626424e98a919668231bbe4f8592fb96b10c73200afd51da32c5aba
|
|
4
|
+
data.tar.gz: 1f9f998876a1224474c411ed06351fec97b044baf04315ffa3207da775b7ce89
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 707a7d1a56421bd9efda59b20da4861dfa7c7733ab64484eceb503a4e5d29b556a5eda81af437edd985ce3ac4d0aec2346f10f819f6109889625e4ec4fcd3e99
|
|
7
|
+
data.tar.gz: 8d01faeb9972d46ea999ee9233388786a8fb132f415a9ddcb7cc41f3ae1ba2be87ffc87fd7e2c7c9551fd715bc96307a64b74b9bffa7bf97543dfe3a3cc3e35b
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.7.1 - 2026-10-01
|
|
6
|
+
|
|
7
|
+
- **会话接口(不兼容 0.6.x)**:本次发布包含 0.7.0 的显式 `Expect::Session`、操作关键字及标准 Logger 接口;
|
|
8
|
+
删除全局 Configuration、委托门面和旧终端包装,升级前请阅读 `docs/MIGRATION.md`。
|
|
9
|
+
- **启动清理**:启动线程在 fork 返回后被中断时,先登记子进程归属,再沿用现有清理流程,避免遗留未回收进程。
|
|
10
|
+
- **回调控制**:超时与 EOF 回调只识别约定的继续控制符,自定义对象的相等运算不再触发重复回调或重置期限。
|
|
11
|
+
- **交互参数**:`interact` 仅把 nil 当作缺省输出或转义,显式传入 false 会报错,避免误用默认输出或静默忽略转义。
|
|
12
|
+
- **代码约定**:内部谓词统一返回布尔值,继续控制符比较不再分配临时数组,并同步 API 检查器命名与贡献文档中的验证入口。
|
|
13
|
+
- **模块注释**:补齐日志与转接宿主协议、资源账本、EOF 与写入游标的职责说明,明确借用对象及失败后的状态归属。
|
|
14
|
+
|
|
15
|
+
## 0.7.0 - 2026-10-01
|
|
16
|
+
|
|
17
|
+
- **公开会话**:`Expect` 改为工厂模块,`Expect::Session` 直接承载会话、回调和结果来源,删除重复委托门面和全局 Configuration。
|
|
18
|
+
- **显式选项**:匹配使用 `consume:`、`reset_timeout_on_read:`,终端使用 `raw:`,关闭使用 `graceful:`;删除对应的常驻布尔开关及过渡接口。
|
|
19
|
+
- **标准日志**:诊断注入标准 Logger,统一级别与格式化协议;原始记录使用借用的 `transcript` writer,文件创建和关闭交给
|
|
20
|
+
`File.open`,流式秘密脱敏继续保留。
|
|
21
|
+
- **输出与终端**:转发目标统一为 `outputs`;移除外部 `stty` 包装,终端模式与窗口大小直接使用 `io-console`。删除无引用的
|
|
22
|
+
forwardable 运行依赖,shellwords 仅用于开发示例。
|
|
23
|
+
- **转接实现**:合并为唯一的排队发送路径,明确待写游标与回调状态;基准改为运行实际 Relay 循环。
|
|
24
|
+
- **清理异常**:已有主异常时,transcript writer 或 Logger formatter 的 StandardError
|
|
25
|
+
不再覆盖它;没有主异常时仍传播清理错误,清理中断不被吞掉,所属子进程正常回收。
|
|
26
|
+
- **交付接口**:同步 RBS、API 文档、示例、安装验证和 0.6.x 迁移说明;API 检查使用方法的文档标注区分内部协议,避免手工维护方法白名单。
|
|
27
|
+
|
|
28
|
+
## 0.6.1 - 2026-09-30
|
|
29
|
+
|
|
30
|
+
- **错误归属**:多来源等待在 EOF 后继续时,后续 IO 选择错误只更新仍活跃的来源,保留已结束来源的 EOF 与尾部结果。
|
|
31
|
+
- **写入进度**:发送前对象转换或诊断中的嵌套写入超时,对当前命令报告零进度,并通过 `cause` 保留嵌套错误的真实进度。
|
|
32
|
+
- **调度性能**:已处理 EOF 使用身份集合,批量转接就绪使用一次性 IO 身份索引,减少多来源场景的重复检索;保留事件、读取和回调顺序。
|
|
33
|
+
- **脱敏性能**:流结束时先排除不可能匹配的秘密前缀长度,减少长尾未命中的扫描和分配;保留部分秘密、重叠及二进制语义。
|
|
34
|
+
- **进程归属**:父进程创建 PTY、fork 后才启动命令时,按实际启动者登记子进程归属,保证等待与关闭能回收该子进程;继承已有 PID
|
|
35
|
+
的副本仍不接管它。
|
|
36
|
+
- **启动清理**:错误管道关闭失败仍尝试另一端,清理错误不再覆盖原始 fork 错误或 `SpawnError`。
|
|
37
|
+
- **来源身份**:匹配分组、EOF 派发、就绪查询与转接按会话和 IO 对象身份处理;自定义值相等规则不再合并独立来源或遗漏可写目标。
|
|
38
|
+
- **转义恢复**:超时交付的字面转义前缀也进入正则历史,混合规则跨次转接时不会漏掉完整退出序列。
|
|
39
|
+
- **写入协议**:自定义监听器返回 `:wait_writable` 时按非法写入计数抛出 `IOError`,该哨兵只用于真实 IO 的非阻塞写入。
|
|
40
|
+
|
|
41
|
+
## 0.6.0 - 2026-09-30
|
|
42
|
+
|
|
43
|
+
- `expect` 统一返回不可变 Result,移除 `expect_result`;保留最近结果的六个便捷读取方法。
|
|
44
|
+
- 布尔配置仅保留 predicate 与 setter;模式声明在匹配前冻结,内部 Pattern 使用私有 Data。
|
|
45
|
+
- 修正 Result 构造及 callable 日志协议的 RBS,API 门禁覆盖模块方法和 Data 构造接口。
|
|
46
|
+
- 明确单字符串命令的 Ruby shell 语义,迁移示例和检查脚本,移除 Perl 差分工具与过时接口迁移表。
|
|
47
|
+
- **Ruby 现代化(破坏性)**:最低 Ruby 3.4;CI 使用 Ruby 3.4/4.0。`Result` 改为不可变 Data,复制冻结文本和捕获值,移除字段写入和
|
|
48
|
+
`to_a`,使用 `to_h` 或模式解构。
|
|
49
|
+
- **职责重组**:用户门面委托内部 Session;日志、终端和交互成为职责模块,Matcher/Relay 直接调用内核协议;回调与结果来源仍为用户会话。
|
|
50
|
+
- **清理可靠性**:统一始终/失败清理作用域,日志交接和终端恢复的常规清理错误不再覆盖原始异常;保留所属资源、PID 与转接恢复语义。
|
|
51
|
+
- **工程门禁**:增加公开 API 的 YARD/RBS 与覆盖检查、README 版本校验;精简 Gem 为运行源码、类型签名及使用文档,开发材料保留在仓库。
|
|
52
|
+
- **独立检查脚本**:API 检查按脚本位置定位项目文件,可从任意工作目录执行;发布工具仅在联网发布时加载 `net/http`。
|
|
53
|
+
- **测试稳定性**:模式解构断言使用 Ruby 原语;Ctrl-C 用例以处理器专用退出码验证信号,不依赖终端可能刷掉的输出或固定 sleep。
|
|
54
|
+
|
|
55
|
+
- 移除多余的 io-wait 依赖和加载,使用 Ruby 内置的 IO 等待方法,避免新版空壳 gem 的弃用警告。
|
|
56
|
+
- 保留标准库运行时依赖下限,去掉无已知兼容性依据的上限,避免限制宿主应用依赖解析。
|
|
57
|
+
|
|
5
58
|
## 0.5.3 - 2026-09-27
|
|
6
59
|
|
|
7
60
|
- **初始化清理**:资源账本建立前的中断也会尝试关闭全部所属 IO,保留借用 IO 与原始异常身份。
|
|
@@ -28,7 +81,8 @@
|
|
|
28
81
|
## 0.4.0 - 2026-09-27
|
|
29
82
|
|
|
30
83
|
- **缓冲与生命周期**:新增只读 `buffer_discarded_bytes`,区分上限裁剪与正常消费;补齐外部回收进程、借用 IO、重复关闭和失败重试的契约回归。
|
|
31
|
-
- **等待总期限**:类和实例的 `expect` / `expect_result` 支持绝对单调时钟 `deadline:`,接收重置与继续回调不能延长总预算,回调后到期也保留未消费文本和已知
|
|
84
|
+
- **等待总期限**:类和实例的 `expect` / `expect_result` 支持绝对单调时钟 `deadline:`,接收重置与继续回调不能延长总预算,回调后到期也保留未消费文本和已知
|
|
85
|
+
EOF 顺序。
|
|
32
86
|
- **诊断与脱敏**:会话级 `diagnostic_output` 支持 Logger、IO 和结构化回调;`redact` 对日志及收发诊断按字节流过滤,覆盖分片、重叠和流尾部,不改写匹配或协议转发。
|
|
33
87
|
- **匹配性能**:字面模式复用同一缓冲代次中已排除的前缀,消费、替换和裁剪后失效;正则保留完整窗口及原有声明优先级。
|
|
34
88
|
- **验证与维护**:增加持续输出、同时就绪来源和阻塞目标基准,记录 RSS 端点和描述符计数;补齐底层模块中文注释与内部契约。
|
data/README.md
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
IO、同时监听多个会话和转接人工交互。交互能力参考 [Expect.pm](https://github.com/jacoby/expect.pm),接口采用 Ruby
|
|
7
7
|
的属性、关键字参数和代码块。
|
|
8
8
|
|
|
9
|
-
要求 **Ruby 3.
|
|
10
|
-
RubyGems/Bundler 解析。推荐入口 **`require "expect/pty"`**;本项目提供独立的 `Expect`
|
|
9
|
+
要求 **Ruby 3.4+、POSIX 系统(Linux/macOS)**。运行时仅使用 Ruby 标准库,其中可独立安装的 gem 已在 gemspec 中声明,由
|
|
10
|
+
RubyGems/Bundler 解析。推荐入口 **`require "expect/pty"`**;本项目提供独立的 `Expect` 模块和 `Expect::Session` 会话,不修改标准库的
|
|
11
|
+
`IO#expect`。
|
|
11
12
|
|
|
12
13
|
源码中的解释性注释主要使用中文;欢迎用中文或英文提交 issue 和 PR,参与方式见 [贡献指南](CONTRIBUTING.md)。
|
|
13
14
|
|
|
@@ -16,7 +17,7 @@ RubyGems/Bundler 解析。推荐入口 **`require "expect/pty"`**;本项目提
|
|
|
16
17
|
项目和仓库名为 `expect-ruby`,Gem 名为 `expect-pty`。在应用的 Gemfile 中添加以下内容,然后运行 `bundle install`:
|
|
17
18
|
|
|
18
19
|
```ruby
|
|
19
|
-
gem "expect-pty", "~> 0.
|
|
20
|
+
gem "expect-pty", "~> 0.7.1", require: "expect/pty"
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
也可直接执行 `gem install expect-pty`。需要跟随开发分支时,可从 GitHub 安装:
|
|
@@ -29,8 +30,8 @@ gem "expect-pty", git: "https://github.com/gatework/expect-ruby.git", branch: "m
|
|
|
29
30
|
|
|
30
31
|
```sh
|
|
31
32
|
mkdir -p tmp
|
|
32
|
-
gem build expect-pty.gemspec --output tmp/expect-pty-0.
|
|
33
|
-
gem install ./tmp/expect-pty-0.
|
|
33
|
+
gem build expect-pty.gemspec --output tmp/expect-pty-0.7.1.gem
|
|
34
|
+
gem install ./tmp/expect-pty-0.7.1.gem
|
|
34
35
|
```
|
|
35
36
|
|
|
36
37
|
```ruby
|
|
@@ -38,7 +39,7 @@ require "expect/pty"
|
|
|
38
39
|
|
|
39
40
|
Expect.spawn("/bin/sh", "-i") do |shell|
|
|
40
41
|
shell.puts("printf 'hello ruby\\n'")
|
|
41
|
-
if shell.expect(/^hello ruby\r?$/, timeout: 3)
|
|
42
|
+
if shell.expect(/^hello ruby\r?$/, timeout: 3).matched?
|
|
42
43
|
puts shell.match
|
|
43
44
|
else
|
|
44
45
|
warn shell.error
|
|
@@ -48,54 +49,43 @@ Expect.spawn("/bin/sh", "-i") do |shell|
|
|
|
48
49
|
end
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
块返回其执行结果,退出时关闭会话并回收子进程,异常和 `break` 也执行清理。构造或启动失败时同样释放已创建的资源;清理中的
|
|
52
|
-
|
|
53
|
-
`slave.
|
|
52
|
+
块返回其执行结果,退出时关闭会话并回收子进程,异常和 `break` 也执行清理。构造或启动失败时同样释放已创建的资源;清理中的
|
|
53
|
+
`StandardError` 不替换本次作用域已有的主异常,没有主异常时仍传播;清理中新发生的 `Interrupt` / `SystemExit` 不吞掉。
|
|
54
|
+
无块形式需用 `ensure` 显式调用 `close`。`Expect::Session.new` 可以先创建 PTY,通过 `slave.echo=` /
|
|
55
|
+
`slave.winsize=` 设置终端,然后调用 `session.spawn`。工厂返回、回调参数和 `Result#session` 都是同一个真实 Session。
|
|
54
56
|
|
|
55
57
|
多个命令参数原样传给 Ruby `exec`;单个命令字符串使用 Ruby 的 shell 语义。不可信参数应使用独立参数形式。支持
|
|
56
58
|
`env: { "NAME" => "value" }` 和 `chdir: "/path"`。同一会话只能启动一次,启动失败抛出 `Expect::SpawnError`。
|
|
57
59
|
|
|
58
|
-
##
|
|
60
|
+
## 会话设置与操作参数
|
|
59
61
|
|
|
60
62
|
```ruby
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
config.graceful_close = true
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
Expect.configure(debug_level: 0) # 也可用关键字修改默认值
|
|
68
|
-
Expect.configuration.timeout # 默认值快照,只读
|
|
69
|
-
|
|
70
|
-
Expect.spawn("/bin/sh", "-i", timeout: 3) do |session|
|
|
63
|
+
logger = Logger.new($stderr, level: Logger::INFO)
|
|
64
|
+
Expect.spawn("/bin/sh", "-i", timeout: 3, buffer_limit: 65_536,
|
|
65
|
+
logger: logger, outputs: [$stdout]) do |session|
|
|
71
66
|
session.timeout = 5
|
|
72
|
-
session.log_stdout = true
|
|
73
|
-
session.raw_pty? # 布尔属性用问号方法查询
|
|
74
67
|
session.puts("exit")
|
|
75
68
|
end
|
|
76
69
|
```
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
所有设置显式传给会话,不提供全局默认配置或配置基类。需要应用默认值时,由调用方保存 Hash,再用关键字展开传入。
|
|
72
|
+
各会话独立保存以下属性,修改不会影响其他会话;借用的 logger、transcript 和输出对象可以由调用方共享。
|
|
80
73
|
|
|
81
|
-
|
|
74
|
+
| 会话属性 | 默认值 | 行为 |
|
|
75
|
+
|-----------------|--------|-------------------------------------------------------|
|
|
76
|
+
| `timeout` | `nil` | `session.expect` 的默认相对期限;`nil` 无限、`0` 轮询 |
|
|
77
|
+
| `write_timeout` | `nil` | 写入遇到背压或 EINTR 时的等待期限 |
|
|
78
|
+
| `buffer_limit` | `nil` | 匹配缓冲保留的尾部字节数;正整数或 `nil` |
|
|
79
|
+
| `logger` | `nil` | 借用支持 `add` / `debug?` 的诊断 logger |
|
|
80
|
+
| `transcript` | `nil` | 借用支持 `write` 的接收字节记录目标 |
|
|
81
|
+
| `outputs` | `[]` | 原始接收字节的转发目标数组,可包含 `$stdout` |
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|-------------------------|---------|-----------------------------------------------------------------|
|
|
85
|
-
| `timeout` | `nil` | 等待匹配的默认超时,秒;`nil` 无限,`0` 非阻塞轮询 |
|
|
86
|
-
| `write_timeout` | `nil` | 写入遇到背压时的超时,秒 |
|
|
87
|
-
| `buffer_limit` | `nil` | 接收缓冲最多保留的字节数;正整数或 `nil`(无限) |
|
|
88
|
-
| `debug_level` | `0` | `1` 生命周期和匹配,`2` 加收发内容,`3` 加缓冲内容 |
|
|
89
|
-
| `raw_pty` | `false` | spawn 前将 slave 设为 raw,禁用回显和换行转换 |
|
|
90
|
-
| `preserve_buffer` | `false` | 匹配后保留完整缓冲 |
|
|
91
|
-
| `log_stdout` | `false` | 将接收内容输出到 `$stdout` |
|
|
92
|
-
| `log_listeners` | `true` | 将接收内容转发给 `listeners` |
|
|
93
|
-
| `raw_terminal` | `true` | `interact` 期间自动设置并恢复输入终端模式,同时保留输出换行处理 |
|
|
94
|
-
| `reset_timeout_on_read` | `false` | 每次收到数据时重置匹配期限 |
|
|
95
|
-
| `graceful_close` | `false` | `close` 先尝试软关闭,再完成强制清理 |
|
|
83
|
+
这些属性都有同名 reader/writer;非法赋值不改变原设置。超时必须有限且非负。输出数组在设置时复制,读取时也返回副本。
|
|
96
84
|
|
|
97
|
-
|
|
98
|
-
|
|
85
|
+
只属于一次操作的策略放在该操作的关键字中:`spawn(raw: false)` 控制子终端,`expect(consume: true,
|
|
86
|
+
reset_timeout_on_read: false)` 控制匹配消费和输入重置,`interact(raw: true)` 控制本地终端模式,
|
|
87
|
+
`close(graceful: false)` 控制清理顺序。工厂的 `graceful:` 决定块退出时的关闭策略。`Session.new` 只接收会话属性,
|
|
88
|
+
命令、`env:`、`chdir:` 和 `raw:` 由之后的 `session.spawn` 接收;均无旧名称别名。
|
|
99
89
|
|
|
100
90
|
## 等待和匹配
|
|
101
91
|
|
|
@@ -109,11 +99,12 @@ session.expect(timeout: 1) # 仅收集输出,直到超时或 EOF
|
|
|
109
99
|
```
|
|
110
100
|
|
|
111
101
|
字符串始终按字面匹配,包括 `"-i"`、`"-re"`、`"timeout"` 和 `"eof"`;正则直接使用 Ruby `Regexp`
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
。按声明顺序选择第一个能匹配的模式,不按它们在文本中的位置排序。返回不可变 `Expect::Result`,`number` 为模式的 **1
|
|
103
|
+
起始序号**;超时、EOF 或 IO 错误时 `number` 为 `nil`。
|
|
104
|
+
判断成功使用 `matched?`,不能直接判断 Result 对象的真值。超时使用单调时钟。
|
|
114
105
|
|
|
115
106
|
```ruby
|
|
116
|
-
result = session.
|
|
107
|
+
result = session.expect(/value=(\d+)/, timeout: 3)
|
|
117
108
|
result.matched?
|
|
118
109
|
result.timeout?
|
|
119
110
|
result.eof?
|
|
@@ -122,17 +113,18 @@ result.captures
|
|
|
122
113
|
result.session
|
|
123
114
|
result.error # nil、:timeout、:eof 或原始 IOError / SystemCallError 对象
|
|
124
115
|
|
|
125
|
-
|
|
116
|
+
result => { number:, match:, captures: }
|
|
126
117
|
```
|
|
127
118
|
|
|
128
|
-
`Result` 使用原生 Ruby `
|
|
119
|
+
`Result` 使用原生 Ruby `Data`,支持 `to_h`、位置和键模式解构。结果、文本和捕获数组均为不可变快照;
|
|
120
|
+
来源会话与原始异常保持原对象。不提供字段写入、`to_a` 或隐式 `to_ary`。会话提供 `last_result`,以及 `match`、
|
|
129
121
|
`before`、`after`、`match_number`、`captures`、`error` 快捷读取方法。
|
|
130
122
|
|
|
131
123
|
成功匹配后删除匹配内容及其之前的内容,尾部留给下次匹配;超时保留缓冲,EOF 将未匹配内容放入 `before` 并清空缓冲。EOF
|
|
132
124
|
与子进程退出是不同事件,使用 `wait` / `process_status` 判断进程结果。底层 IO 错误保留原始异常,回调中的普通异常直接抛出。
|
|
133
125
|
|
|
134
126
|
接收缓冲、匹配和捕获值为 `ASCII-8BIT` 字节串,保留控制字符、NUL 和无效 UTF-8。固定 UTF-8
|
|
135
|
-
正则会等待读取末尾拆开的字符收齐后再匹配,以免尾部锚点提前命中;显示捕获内容时可 `.force_encoding("UTF-8")`
|
|
127
|
+
正则会等待读取末尾拆开的字符收齐后再匹配,以免尾部锚点提前命中;显示捕获内容时可 `.dup.force_encoding("UTF-8")`
|
|
136
128
|
。任意二进制流请用字面字符串或二进制正则 `/.../n`。固定 UTF-8 正则遇到无效数据抛出 `EncodingError`;EOF
|
|
137
129
|
时仍未收齐的字符也属于无效编码,匹配缓冲保留原字节供诊断或二进制匹配。缓冲上限按字节截断,应为文本设置足够的上限。
|
|
138
130
|
|
|
@@ -149,16 +141,15 @@ IO 等待的 `timeout` 不会中断单次正则计算。处理用户提供的正
|
|
|
149
141
|
|
|
150
142
|
## 回调、事件与多会话
|
|
151
143
|
|
|
152
|
-
|
|
153
144
|
```ruby
|
|
154
145
|
session.expect(timeout: 10) do
|
|
155
146
|
on(/username:\s*/i) do |connection|
|
|
156
147
|
connection.puts("demo")
|
|
157
|
-
|
|
148
|
+
Expect.continue
|
|
158
149
|
end
|
|
159
150
|
on(/password:\s*/i) do |connection|
|
|
160
151
|
connection.puts(password)
|
|
161
|
-
|
|
152
|
+
Expect.continue(reset_timeout: false)
|
|
162
153
|
end
|
|
163
154
|
on("ready>")
|
|
164
155
|
eof { |connection| warn "EOF: #{connection.before}" }
|
|
@@ -167,21 +158,19 @@ end
|
|
|
167
158
|
```
|
|
168
159
|
|
|
169
160
|
回调通过闭包访问局部变量。无参数声明块在模式构建器中执行;希望保留调用方 `self` 时使用 `do |patterns|`,调用
|
|
170
|
-
`patterns.on(...)`。所有模式注册完成后才读取 IO;注册异常或 `break`
|
|
171
|
-
支持同样的声明方式。
|
|
161
|
+
`patterns.on(...)`。所有模式注册完成后才读取 IO;注册异常或 `break` 不消费输入。块和位置模式不能混用。注册完成后规则冻结,回调不能再向本次等待追加模式。
|
|
172
162
|
|
|
173
|
-
`continue` 继续等待并重新计时;`continue(reset_timeout: false)`
|
|
174
|
-
|
|
175
|
-
`continue` 才再次等待。EOF 回调继续时移除该源并等待其余会话;已知的 EOF 仍依次派发,全部 EOF 时直接返回,期限已过时仅对剩余活跃源触发超时。
|
|
163
|
+
`Expect.continue` 继续等待并重新计时;`Expect.continue(reset_timeout: false)`
|
|
164
|
+
保留原期限。回调返回后若保留的期限已过,不再扫描新的文本匹配,未消费的输入留给下一次等待。无回调或返回其他值时结束本次匹配。超时回调只有返回重置计时的
|
|
165
|
+
`Expect.continue` 才再次等待。EOF 回调继续时移除该源并等待其余会话;已知的 EOF 仍依次派发,全部 EOF 时直接返回,期限已过时仅对剩余活跃源触发超时。
|
|
176
166
|
|
|
177
|
-
`expect`
|
|
167
|
+
`expect` 另接受 `deadline:`,值为 `Expect.monotonic` 时钟上的绝对秒数,`nil` 表示不设总期限。总期限与普通
|
|
178
168
|
`timeout` 取较早者,接收重置、文本/EOF 继续及超时回调均不能延长它。同一个 deadline 可用于连续多次等待:
|
|
179
169
|
|
|
180
170
|
```ruby
|
|
181
171
|
deadline = Expect.monotonic + 60
|
|
182
|
-
session.reset_timeout_on_read
|
|
183
|
-
session.expect("
|
|
184
|
-
session.expect("done", timeout: 5, deadline: deadline)
|
|
172
|
+
session.expect("ready", timeout: 5, deadline: deadline, reset_timeout_on_read: true)
|
|
173
|
+
session.expect("done", timeout: 5, deadline: deadline, reset_timeout_on_read: true)
|
|
185
174
|
```
|
|
186
175
|
|
|
187
176
|
上例每次等待允许最多 5 秒无新输入,两次等待共用 60 秒总预算。到达总期限后返回超时,不消费已缓冲的文本或读取新数据;已经确认的
|
|
@@ -200,10 +189,9 @@ end
|
|
|
200
189
|
Expect.expect("ready", from: [first, second], timeout: 5)
|
|
201
190
|
```
|
|
202
191
|
|
|
203
|
-
`from:`
|
|
204
|
-
`Expect.configuration.timeout`。
|
|
192
|
+
`from:` 指定一个或多个会话;实例块默认当前会话,模块方法需提供来源。相邻且来源列表按对象身份及顺序相同的模式组成一组,按组、会话、模式顺序匹配。模块方法省略超时为无限等待。
|
|
205
193
|
|
|
206
|
-
`
|
|
194
|
+
`expect(consume: false)` 时,继续回调通常应自行消费匹配,例如 `connection.buffer = connection.after`
|
|
207
195
|
。如果回调没有改变缓冲,当前模式会等待缓冲变化后才重新匹配,避免反复处理同一内容。被信号中断的匹配 select/read 会自动重试,保留原期限。
|
|
208
196
|
|
|
209
197
|
## 已有 IO、写入和终端
|
|
@@ -218,7 +206,8 @@ ready = Expect.readable_sessions(first, second, timeout: 5)
|
|
|
218
206
|
```
|
|
219
207
|
|
|
220
208
|
`Expect.open` 支持可 `select` 的 File、管道、Socket 和 PTY,`writer:` 可指定独立写端。默认借用 IO,关闭会话不关闭原始 IO;
|
|
221
|
-
`own: true`
|
|
209
|
+
`own: true` 转移关闭责任,初始化中的属性校验失败也会释放接管的 IO。未知关键字在 Ruby 调用入口拒绝,此时不接管 IO。`StringIO`
|
|
210
|
+
可以用作 transcript 和 outputs,不能用作读取会话。
|
|
222
211
|
|
|
223
212
|
用于写入或转接的真实 IO 应在首次写入前设置 `io.sync = true`,并由调用方保证没有未刷新的 Ruby 写缓冲;`write_nonblock`
|
|
224
213
|
可能先阻塞刷新已有缓冲,这一步不受本库的 IO 等待期限控制。已有缓冲应在交付给本库前由调用方排空,库不会绕过缓冲或改变字节顺序。
|
|
@@ -226,17 +215,17 @@ ready = Expect.readable_sessions(first, second, timeout: 5)
|
|
|
226
215
|
`readable_sessions` 返回可读的会话对象数组,不消费数据、不包含已关闭会话、同一会话只返回一次。默认 `timeout: 0`;`nil`
|
|
227
216
|
无限等待。同一会话应由一个读取者驱动,多会话共同监听使用 `Expect.expect`。
|
|
228
217
|
|
|
229
|
-
| API | 行为
|
|
230
|
-
|
|
231
|
-
| `write(*objects)` | 通过 `to_s` 转换并写入所有字节,返回字节数
|
|
232
|
-
| `puts(*objects)` | 原生 IO 风格的换行、数组递归和 `nil` 返回值
|
|
233
|
-
| `session << object` | 写入并返回会话,可链式追加
|
|
234
|
-
| `send_slow(*objects, delay:)` | 每个字符之前等待指定秒数,同时收集返回数据
|
|
235
|
-
| `buffer` / `buffer=` | 获取副本 / 复制字节并应用上限
|
|
236
|
-
| `clear_buffer` | 清空缓冲并返回旧内容
|
|
237
|
-
| `
|
|
238
|
-
| `winsize` / `winsize=`
|
|
239
|
-
| `slave` / `tty_name` / `to_io` / `writer` / `fileno` / `tty?` | 底层 IO 和终端信息
|
|
218
|
+
| API | 行为 |
|
|
219
|
+
|---------------------------------------------------------------|---------------------------------------------|
|
|
220
|
+
| `write(*objects)` | 通过 `to_s` 转换并写入所有字节,返回字节数 |
|
|
221
|
+
| `puts(*objects)` | 原生 IO 风格的换行、数组递归和 `nil` 返回值 |
|
|
222
|
+
| `session << object` | 写入并返回会话,可链式追加 |
|
|
223
|
+
| `send_slow(*objects, delay:)` | 每个字符之前等待指定秒数,同时收集返回数据 |
|
|
224
|
+
| `buffer` / `buffer=` | 获取副本 / 复制字节并应用上限 |
|
|
225
|
+
| `clear_buffer` | 清空缓冲并返回旧内容 |
|
|
226
|
+
| `to_io.console_mode` / `to_io.console_mode=` | 原生终端模式快照与恢复 |
|
|
227
|
+
| `to_io.winsize` / `to_io.winsize=` | 原生 `[rows, cols]` 窗口尺寸接口 |
|
|
228
|
+
| `slave` / `tty_name` / `to_io` / `writer` / `fileno` / `tty?` | 底层 IO 和终端信息 |
|
|
240
229
|
|
|
241
230
|
`send_slow` 在每次写入后只检查已经可读的回复,不附加固定等待;返回时不保证收齐最后一个字符引发的回复,完整对话请继续使用
|
|
242
231
|
`expect`。
|
|
@@ -250,49 +239,45 @@ ready = Expect.readable_sessions(first, second, timeout: 5)
|
|
|
250
239
|
它与匹配的 `timeout/deadline`、整次 `interconnect` 的总 `timeout` 分别计算。非空写入要求底层返回实际接受的正整数字节数,
|
|
251
240
|
且不能超过本次片段长度;非法计数立即抛出 `IOError`,空写入仍返回 0。
|
|
252
241
|
|
|
253
|
-
|
|
254
|
-
|
|
242
|
+
终端操作直接使用 Ruby `io/console`:例如 `session.to_io.echo = false`、`session.to_io.winsize = [24, 80]`。
|
|
243
|
+
需要作用域恢复时使用原生 `raw` / `cooked` 块;本库不启动 `stty` 子进程。
|
|
255
244
|
|
|
256
|
-
##
|
|
245
|
+
## 诊断、接收记录与人工交互
|
|
257
246
|
|
|
258
247
|
```ruby
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
session.
|
|
263
|
-
|
|
264
|
-
session.
|
|
265
|
-
session.
|
|
266
|
-
session.
|
|
248
|
+
require "logger"
|
|
249
|
+
|
|
250
|
+
logger = Logger.new($stderr, level: Logger::DEBUG)
|
|
251
|
+
File.open("session.log", File::WRONLY | File::CREAT | File::APPEND, 0o600) do |transcript|
|
|
252
|
+
Expect.spawn("/bin/sh", "-i", logger: logger, transcript: transcript) do |session|
|
|
253
|
+
session.redact(password, token) # 在首次通信前注册需要保护的原始字节
|
|
254
|
+
session.outputs = [$stdout, another_session]
|
|
255
|
+
session.write_transcript("annotation\n")
|
|
256
|
+
session.puts("exit")
|
|
257
|
+
end
|
|
258
|
+
end
|
|
267
259
|
```
|
|
268
260
|
|
|
269
|
-
|
|
270
|
-
|
|
261
|
+
三类目标职责独立:`logger` 接收结构化诊断;`transcript` 仅记录实际读取的接收字节;`outputs` 原样转发协议字节。
|
|
262
|
+
所有目标均由调用方创建和关闭。`transcript = nil` 停止记录,替换前先交付旧流过滤尾部;`write_transcript` 补写记录,返回
|
|
263
|
+
`nil`,
|
|
264
|
+
不发送到子进程。writer 必须返回实际接受的字节数,支持短写;路径与 callable 不会自动包装成 writer。
|
|
271
265
|
|
|
272
|
-
|
|
266
|
+
`logger` 采用 Ruby Logger 的 `add` / `debug?` 协议,默认 `nil` 禁用。Logger 自己决定级别、格式及输出位置;可直接注入兼容该协议的
|
|
267
|
+
ActiveSupport logger,本库不依赖 ActiveSupport。生命周期和匹配使用 INFO,收发字节使用 DEBUG,不生成缓冲快照诊断。
|
|
268
|
+
`add` 的 message 是冻结的 Hash,包含 `event`、`pid`、`fd`、`message`,其中 message 字符串也被冻结;progname 为 `"Expect"`。
|
|
269
|
+
自定义格式使用 Logger 的 formatter;事件不含会话对象。logger 的返回值不控制匹配。
|
|
273
270
|
|
|
274
|
-
|
|
275
|
-
|
|
271
|
+
所有会话默认没有 stdout 输出。需要显示时显式把 `$stdout` 放进 `outputs`。普通 `expect` 按读取顺序同步执行诊断、
|
|
272
|
+
transcript 和 outputs,目标须及时消费;匹配期限不会中断这些代码。慢目标需要独立调度时使用 `interconnect`。
|
|
273
|
+
记录不重复包含发送字节,但终端回显可能作为接收内容返回;敏感会话须控制回显、显示和目标文件权限。
|
|
276
274
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
增加缓冲。Logger 的级别分别使用 `info` 和 `debug`;回调收到冻结的 Hash,包含 `event`、`level`、`pid`、`fd`、`message`,不包含会话对象。
|
|
275
|
+
`redact` 复制并追加非空字符串秘密,以 `[FILTERED]` 遮盖 transcript、`write_transcript` 及收发诊断,支持跨分片和重叠秘密。
|
|
276
|
+
匹配缓冲、Result 和 outputs 始终保留原始字节。它不推断编码、终端转义、哈希或其他变换后的秘密,也不能删除已交付的记录。
|
|
280
277
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
session.debug_level = 2
|
|
285
|
-
session.redact(password, token) # 在首次通信前注册需要保护的原始字节
|
|
286
|
-
session.log_to("session.log")
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
`redact` 复制并追加本会话的非空字符串秘密,以 `[FILTERED]` 遮盖 `log_to` / `write_log`
|
|
290
|
-
及诊断中的对应字节,支持跨读取、跨写入和重叠秘密;匹配缓冲、结果、stdout 显示和 listeners 的转发仍是原始字节。应继续关闭敏感会话的
|
|
291
|
-
`log_stdout`,并自行保护交互显示及协议转发目标。它不推断编码、终端转义、哈希或其他变换后的秘密,也不能删除已输出的日志。
|
|
292
|
-
|
|
293
|
-
过滤器最多延迟最长秘密长度减一的尾部字节,EOF、目标替换和显式关闭时交付剩余内容;流边界处疑似秘密前缀也会被遮盖。发送与接收诊断各自保留过滤状态;启用脱敏时,level
|
|
294
|
-
3 的缓冲快照只显示 `[FILTERED]`,避免部分消费或裁剪后剩下的秘密片段绕过过滤。日志回调的分块边界因此可能变化。GC
|
|
295
|
-
兜底不会调用用户日志回调,需显式关闭会话以交付过滤器尾部。
|
|
278
|
+
过滤器最多延迟最长秘密长度减一的尾部字节,EOF、目标替换和显式关闭时交付剩余内容;流边界的疑似秘密前缀也会遮盖。
|
|
279
|
+
发送与接收诊断独立保留过滤状态,因此实际写入分块可能变化。GC 兜底不调用用户代码;需显式关闭会话以交付过滤尾部,
|
|
280
|
+
然后由调用方关闭 transcript 和 logger。
|
|
296
281
|
|
|
297
282
|
应用需要过滤自己的日志或错误文本时,可以直接使用独立的字节过滤器,无需打开 PTY 或创建会话:
|
|
298
283
|
|
|
@@ -315,8 +300,8 @@ output.write(filter.finish)
|
|
|
315
300
|
session.interact(input: $stdin, escape: "\x1d", output: $stdout) # Ctrl-]
|
|
316
301
|
|
|
317
302
|
Expect.open($stdin) do |input|
|
|
318
|
-
input.
|
|
319
|
-
session.
|
|
303
|
+
input.outputs = [session]
|
|
304
|
+
session.outputs = [$stdout]
|
|
320
305
|
input.on_sequence("\x1d") { false }
|
|
321
306
|
Expect.interconnect(input, session, timeout: 60)
|
|
322
307
|
end
|
|
@@ -327,28 +312,28 @@ end
|
|
|
327
312
|
|
|
328
313
|
`interconnect` 统一调度真实 IO 的非阻塞读写;慢目标不会阻止其他源前进,等待同时受总 `timeout` 和目标会话的 `write_timeout`
|
|
329
314
|
约束。总期限到达返回 `nil`,目标写期限先到则抛出 `WriteTimeout`。超时后的字面转义前缀只尝试非阻塞发送,不再等待下游。作为写入目标但未显式列出的
|
|
330
|
-
|
|
315
|
+
Session,背压期间读取的回复保留在其匹配缓冲;需要同时转发这些回复时,把它也传给 `interconnect`。
|
|
331
316
|
|
|
332
317
|
每个源独立保存待发送数据以及各目标的发送位置,`source.pending_output?` 表示仍有未交付内容。超时或异常后再次对同一源调用
|
|
333
318
|
`interconnect`,会接着发送未完成的后缀,已完成的目标不会重复接收;转义回调在前缀交付后执行。待发送数据与 `buffer`
|
|
334
|
-
中尚未处理的输入分开保存,修改 `
|
|
319
|
+
中尚未处理的输入分开保存,修改 `outputs` 仅影响后续数据,旧数据仍发往原目标。恢复时不要把原始数据再次赋给 `buffer`
|
|
335
320
|
,也不要在排空旧输出前插入新的直接写入;关闭源会话会放弃其待发送数据。转接保留的输入暂不按 `buffer_limit` 裁剪,下次匹配时重新应用该上限。
|
|
336
321
|
|
|
337
322
|
自定义写入对象必须及时返回实际接受的字节数,短写入会继续发送后缀,零、负数或非法返回值抛出 `IOError`
|
|
338
323
|
。对象若先写入再抛错而不报告进度,库无法推断其副作用。日志、用户回调及自定义 `write` / `flush` 同步运行,应由调用方保证它们不会无限阻塞;上述
|
|
339
|
-
IO 期限不会强行中断这些代码。普通 `expect`
|
|
324
|
+
IO 期限不会强行中断这些代码。普通 `expect` 的同步 transcript 和 outputs 也不受匹配等待期限限制。
|
|
340
325
|
|
|
341
326
|
字面转义可以跨读取完整过滤,尾部留给下次调用。正则转义使用历史记录,默认最多保留最近 65,536 字节;设置 `buffer_limit`
|
|
342
327
|
后改用该值。正则及其锚点作用于当前历史窗口,超过窗口的跨读取正则无法匹配,已实时转发的前缀也无法撤回;零长度正则匹配抛出
|
|
343
328
|
`ArgumentError`。日志包括被转接过滤的转义,显式启用 `redact` 时遮盖注册秘密;在 `expect` / `interconnect` 之间切换不会重复记录。
|
|
344
329
|
|
|
345
330
|
一次转接尚未返回时,递归 `interconnect` 的来源若与活跃来源重叠,会在移动缓冲和修改发送游标前抛出
|
|
346
|
-
`Expect::ReentrancyError`。完全独立的来源仍可嵌套转接;`on_sequence` 中的嵌套 `expect
|
|
331
|
+
`Expect::ReentrancyError`。完全独立的来源仍可嵌套转接;`on_sequence` 中的嵌套 `expect` 及返回后再次转接仍受支持。
|
|
347
332
|
自定义 `write` 若已产生副作用却抛错、未返回计数,库无法推断已接受的字节数,此时不能保证恢复交付恰好一次。
|
|
348
333
|
这一保护不代表所有会话 API 都可以跨线程并发调用。
|
|
349
334
|
|
|
350
|
-
`interact`
|
|
351
|
-
`interconnect` 只负责字节转发,由调用方管理终端模式。`interact`
|
|
335
|
+
`interact` 默认 `raw: true`,自动设置并恢复本地输入终端模式,同时保留输出换行处理;`raw: false` 将设置交给调用方。
|
|
336
|
+
通用的 `interconnect` 只负责字节转发,由调用方管理终端模式。`interact` 会恢复临时 outputs 和转义设置,包括超时和异常路径。
|
|
352
337
|
|
|
353
338
|
对同一连接重复传入同一个原始输入 IO 时,`interact` 会复用输入包装器,接续上次预读的尾部。包装器由该连接持有,关闭连接时释放,但不关闭借用的原始
|
|
354
339
|
IO;已关闭的输入或包装器不再复用。需要跨连接共享或自行管理输入生命周期时,显式传入 `Expect.open(input)` 创建的会话。
|
|
@@ -368,7 +353,7 @@ session.close(graceful: true) # 先软关闭,必要时继续硬关闭
|
|
|
368
353
|
- `hard_close`:立即关闭所属 IO,不收集尾部输出;等待 `timeout:`,必要时发送 TERM 再等待同样时长,仍未退出则 KILL 并最多等待
|
|
369
354
|
1 秒。默认 `timeout: 0.2`,必须有限。
|
|
370
355
|
- 两者返回已回收的 `Process::Status`,没有子进程或尚未回收时返回 `nil`;重复调用保留已获得的状态。借用 IO 不关闭。
|
|
371
|
-
- `close(graceful:
|
|
356
|
+
- `close(graceful: false)`:可选先软关闭,`ensure` 中硬关闭,返回 `nil`。块生命周期使用它完成清理;软关闭发生日志异常时也会回收子进程。
|
|
372
357
|
- `wait(timeout: nil)`:等待并回收,返回 `Process::Status`;超时返回 `nil`。`process_status` 非阻塞查询,`exit_code`
|
|
373
358
|
读取普通退出码,信号退出看 `process_status.termsig`。
|
|
374
359
|
- `closed?` 表示会话 IO 已关闭;`alive?` / `pid` 表示子进程状态。软关闭后可能同时 `closed? == true`、`alive? == true`。成功回收后
|
|
@@ -378,10 +363,8 @@ session.close(graceful: true) # 先软关闭,必要时继续硬关闭
|
|
|
378
363
|
|
|
379
364
|
## 安全注意事项
|
|
380
365
|
|
|
381
|
-
- 将不可信命令和参数分别传给 `spawn`,例如 `Expect.spawn("ssh", host)`;单个命令字符串会使用 Ruby 的 shell
|
|
382
|
-
|
|
383
|
-
- 会话日志可能记录密码回显、令牌和其他敏感字节。新日志文件以 `0600` 创建,已有文件保留原权限;请按需关闭 `log_to`、
|
|
384
|
-
`log_stdout` 和调试输出,并管理日志留存。
|
|
366
|
+
- 将不可信命令和参数分别传给 `spawn`,例如 `Expect.spawn("ssh", host)`;单个命令字符串会使用 Ruby 的 shell 语义。
|
|
367
|
+
- 接收记录和诊断可能包含密码回显、令牌和其他敏感字节。调用方负责文件权限与留存,按需关闭 transcript、outputs 和 logger。
|
|
385
368
|
- `spawn` 在子进程中使用 `fork` 后的 Ruby 操作与 `exec`。高度多线程的宿主进程,尤其使用第三方 C 扩展时,可能受到 fork
|
|
386
369
|
时其他线程持锁的影响;尽量在启动其他线程前创建会话,并在自己的运行环境中验证。
|
|
387
370
|
- 不可信正则可能耗费较长时间;使用带 `timeout:` 的 `Regexp` 实例,并为匹配缓冲设置合适的 `buffer_limit`。普通 `expect` 的
|
|
@@ -404,7 +387,7 @@ ruby examples/ssh_interact.rb --auto
|
|
|
404
387
|
的双终端示例与验证见 [examples/kibitz/](examples/kibitz/README.md)。
|
|
405
388
|
|
|
406
389
|
[GitHub Actions](https://github.com/gatework/expect-ruby/actions/workflows/ci.yml) 在推送 `main`、推送 `v*` 标签、提交到
|
|
407
|
-
`main` 的 Pull Request 或手动触发时运行。流水线覆盖 Ubuntu 24.04 / macOS 15 与 Ruby 3.
|
|
390
|
+
`main` 的 Pull Request 或手动触发时运行。流水线覆盖 Ubuntu 24.04 / macOS 15 与 Ruby 3.4、4.0 的 4 种组合;每个环境执行
|
|
408
391
|
`script/ci`,包括真实 PTY 测试和构建包的隔离安装验证。Ubuntu / Ruby 4.0 作业保留已验证的 Gem 构建产物 14 天,可从该次工作流的
|
|
409
392
|
Artifacts 下载。
|
|
410
393
|
|
|
@@ -416,14 +399,13 @@ Artifacts 下载。
|
|
|
416
399
|
运行时依赖只在 `expect-pty.gemspec` 声明,开发依赖放在 Gemfile 的 `development` / `test` 组;安装或使用本库不会引入
|
|
417
400
|
Minitest、Rake、RuboCop 及发布工具的依赖。
|
|
418
401
|
|
|
419
|
-
| 运行时模块
|
|
420
|
-
|
|
421
|
-
| `
|
|
422
|
-
| `
|
|
423
|
-
| `
|
|
424
|
-
| `
|
|
425
|
-
| `
|
|
426
|
-
| `pty` | Ruby 自带扩展 | POSIX 伪终端,无独立 gem |
|
|
402
|
+
| 运行时模块 | Gem | 用途 |
|
|
403
|
+
|--------------------|---------------|--------------------------|
|
|
404
|
+
| `io/console` | `io-console` | 终端模式和窗口大小 |
|
|
405
|
+
| `IO#wait_readable` | Ruby 3.2 内置 | IO 可读等待,无独立 gem |
|
|
406
|
+
| `logger` | `logger` | 标准诊断协议与级别 |
|
|
407
|
+
| `stringio` | `stringio` | Ruby `puts` 语义 |
|
|
408
|
+
| `pty` | Ruby 自带扩展 | POSIX 伪终端,无独立 gem |
|
|
427
409
|
|
|
428
410
|
仅发布 RubyGems 使用 `ruby script/release.rb --rubygems-only`,直接复用本机已有的 Gem 登录状态;添加 `--dry-run`
|
|
429
411
|
可先完成本地检查、测试、构建和安装验证。需要同时创建 GitHub Release 时使用 `ruby script/release.rb`,也可以在 GitHub Actions
|
|
@@ -435,6 +417,16 @@ known_hosts 文件。`ssh_auto.rb` 顶部 `COMMANDS` 可直接修改,日志写
|
|
|
435
417
|
多脚本验证入口为 `test/integration/ssh_scripts.rb`,人工/自动接管入口为 `examples/ssh_interact.rb`
|
|
436
418
|
,详细配置及日志检查见 [SSH 测试说明](test/integration/README.md)。
|
|
437
419
|
|
|
438
|
-
|
|
420
|
+
当前行为边界见 [接口说明](docs/COMPATIBILITY.md),本次与历史验证分列在 [验证记录](docs/VERIFICATION.md)
|
|
439
421
|
。此次重构直接移除了旧入口,不提供兼容别名。
|
|
440
422
|
防火墙连接器已迁移到相邻的 `algosec` 项目;本库只保留 `Expect` 与 `expect-pty` 通用传输能力。
|
|
423
|
+
|
|
424
|
+
## 类型、文档与 0.7.0 接口变更
|
|
425
|
+
|
|
426
|
+
0.7.0 将 `Expect` 改为模块,公开真实 `Session`,移除全局配置、门面、终端命令包装和旧日志接口。
|
|
427
|
+
最低仍为 Ruby 3.4,保留不可变 `Result`。完整接口见 [API 文档](docs/API.md),从 0.6.x 或更早版本升级前请阅读
|
|
428
|
+
[迁移说明](docs/MIGRATION.md)。类型签名随 Gem 发布在 `sig/expect.rbs`。
|
|
429
|
+
|
|
430
|
+
发布包仅包含运行源码、类型签名和使用文档。测试、基准、示例及维护脚本请从仓库取得;
|
|
431
|
+
运行 `bundle exec rake api` 验证文档覆盖与 RBS 声明,运行 `bundle exec yard doc --output-dir tmp/yard`
|
|
432
|
+
生成 HTML 文档。签名验证不代表全库已经通过静态类型检查。
|