为何`stty cols 40`在bash与xonsh shell中存在行为差异
问题根本原因
首先明确两个基础逻辑:
stty默认操作目标是自身标准输入(stdin)关联的终端设备,通过TIOCSWINSZ接口修改终端窗口属性后,系统会向该终端的前台进程组发送SIGWINCH(窗口变化信号)。- Bash交互模式下,
stdin/stdout/stderr全部直接绑定用户的真实终端(如/dev/pts/0),没有额外重定向。执行stty cols 40时修改的就是真实终端的属性,所有打开该终端的文件描述符读取到的宽度自然都是修改后的值,符合预期。
而xonsh的行为差异来自于交互模式的设计:
为了支持异步输出、提示符自动重绘等功能,xonsh会自行创建一对伪终端(PTY):将自身的stdout/stderr绑定到这对PTY的从端,主端由xonsh核心循环持有,再把PTY的输出转发到真实终端。而xonsh的stdin仍然直接绑定到真实终端。
执行stty cols 40时的流程为:
stty作为xonsh的子进程,继承的stdin是真实终端,因此修改的是真实终端的宽度为40,测试程序读取stdin的宽度自然是40。- 执行
stty时它自身成为真实终端的前台进程,修改完成后系统发出的SIGWINCH只会发给stty所在的前台进程组,xonsh主进程收不到这个信号,因此不会将自身创建的PTY的宽度同步为40,仍然保持初始的80。子进程继承的stdout/stderr是这个未同步的PTY的描述符,因此读取到的宽度仍然是80,和测试结果完全吻合。
解决方法
临时单次生效
有两种操作方式可以快速统一三个流的宽度:
- 方式一:先执行常规
stty cols 40修改真实终端宽度,再执行kill -WINCH $$($$在xonsh中对应当前shell主进程的PID),触发xonsh同步自身PTY的宽度到真实终端的最新设置,此时所有子进程的三个标准流宽度都会变为40。 - 方式二:先执行
tty获取xonsh自身stdout对应的PTY路径(输出类似/dev/pts/3),再同时对真实终端和该PTY执行设置:stty cols 40 && stty -F $(tty) cols 40,一步完成两个终端的宽度修改。
长期便捷使用
如果经常需要调整终端宽度,可以在xonsh配置文件~/.xonshrc中添加别名,把上述两步操作合并:alias sttyx = "stty $@ && kill -WINCH $$"
之后只需要执行sttyx cols 40,就能一次性完成真实终端修改和xonsh PTY同步。
可选:对齐bash行为
如果你不需要xonsh的异步输出排版、提示符自动重绘等高级功能,可以在~/.xonshrc中添加配置,让xonsh不再为子进程分配PTY,直接将子进程的stdout/stderr绑定到真实终端,行为就和bash完全一致:$XONSH_DISABLE_PTY = True
注意:该配置会导致xonsh失去对后台任务输出的对齐排版、切换窗口/命令时的提示符自动重绘等能力,按需选择。
内容的提问来源于stack exchange,提问作者halloleo
相关产品推荐
相关产品推荐

