惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MyScale Blog
MyScale Blog
Apple Machine Learning Research
Apple Machine Learning Research
H
Help Net Security
雷峰网
雷峰网
V
Visual Studio Blog
G
Google Developers Blog
Microsoft Azure Blog
Microsoft Azure Blog
Hugging Face - Blog
Hugging Face - Blog
爱范儿
爱范儿
IT之家
IT之家
Engineering at Meta
Engineering at Meta
Microsoft Security Blog
Microsoft Security Blog
aimingoo的专栏
aimingoo的专栏
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
月光博客
月光博客
A
About on SuperTechFans
B
Blog RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The GitHub Blog
The GitHub Blog
N
Netflix TechBlog - Medium
J
Java Code Geeks
云风的 BLOG
云风的 BLOG
Blog — PlanetScale
Blog — PlanetScale

依云's Blog

Wayfire支持不缩放Xwayland啦 - 依云's Blog 使用wayvnc远程访问无头Wayfire会话 - 依云's Blog pacfiles: 高速的 pacman -F 替代品 用 Android 手机当电脑的话筒 - 依云's Blog 使用 ffmpeg 对音频文件进行响度归一化 - 依云's Blog 使用 nftables 屏蔽大量 IP - 依云's Blog YubiKey 初体验 - 依云's Blog fcitx5 码表同步方案 - 依云's Blog 我正在使用的火狐扩展(2024年版) - 依云's Blog 使用 PipeWire 实现自动应用均衡器 如果你发现你的 OOM Killer 在乱杀进程 使用 atuin 管理 shell 命令历史 btrfs 元数据满了怎么办 btrfs 翻车记 在 nspawn 里运行 docker Linux 上的字体配置与故障排除 新的 PaddleOCR 部署方案 使用 EasyEffects 调整 Bose 音箱的体验 让离线软件真正离线 我所讨厌的网页行为 tmux 状态栏优化 Google Chrome 中的字体设置 从 getmail6 到 offlineimap 微信消息通知的困扰 Qt 的字体渲染问题 Wayfire 迁移进展(四):不那么 high 的 DPI Wayfire 迁移进展(二):Xwayland HiDPI 以及 waybar Wayfire 迁移进展 不同情况下的图形效果 Wayland 初体验
使用 Python 自制 expect 功能
依云 · 2014-09-29 · via 依云's Blog

Tcl 的 expect 工具是一个十分有用的自动化工具,经常被用来喂 ssh 密码什么的。不过配置 ssh Control Master英文介绍, 中文翻译之后,我发现第一次连接之后退出,expect 脚本不肯退出了,而且Ctrl-C什么的都不管用,除非我杀掉实际连接到远程主机的 ssh 控制进程。很显然,这是因为 ssh fork 出来的 ssh 进程依旧保持着伪终端的打开状态,expect 依旧在等待来自其上的输出。

研究之后,我发现虽然我可以给我的 expect 脚本加上命令行参数的处理之类的功能,但是interact之后必须等待伪终端关闭没有办法绕过。罢了,本来对 Tcl 就不熟,拿 Python 重写一个吧。

Python 也有个类似的库,叫 pexpect,不过上次我看它的时候还只支持 Python 2,而且使用起来似乎有点问题。所以干脆自制一个满足自己需求的好了。

程序不长,一百多行,但也比预期的长了不少。主要都是些终端和文件描述符的处理。哦还有信号。

我以是否存在子进程来作为判断是否结束interact的标志,而不管是不是还有进程在用这个伪终端。在收到SIGCHLD信号时使用waitid系统调用可以清理任意一已终止的子进程,使用WNOHANG标志在有子进程但是没有已退出者时不要等待、直接返回。于是,在有子进程退出时,反复调用waitid直到它报错就说明已经没有子进程存在啦。

另一个需要处理的信号是SIGWINCH,就是终端大小改变时以此终端为控制终端的前台进程会收到的那个信号。当脚本所在的终端大小改变时,需要重新设置脚本创建的伪终端的大小。不知道为什么,pexpect 竟然没有自己处理这个。

代码照旧在 GitHub 上。以下是使用示例:

#!/usr/bin/env python3

import sys

import expect

def main(host):
  p = expect.Expect()
  p.spawn(['ssh', host])
  p.expect_line('# ')
  p.send('. ./tide\n')
  p.send('TERM=screen-256color exec zsh -l\n')
  p.interact()

if __name__ == '__main__':
  host = 'phone'
  if len(sys.argv) == 2:
    host += sys.argv[1]
  main(host)

这是之前有问题的 expect 脚本:

#!/usr/bin/expect -f

set host [if {$argc eq 0} {lindex phone} {lindex phone$argv}]
spawn -noecho ssh $host
expect "*# "
send ". ./tide\n"
send "TERM=screen-256color exec zsh -l\n"
interact

这是我的 ssh control master 相关配置:

ControlPath ~/.ssh/master-%r@%h:%p
ControlMaster auto
ControlPersist yes