macOS下Bash 3.2中命令替换内重定向stderr时tput cols异常问题
问题原因解析
这是Bash命令替换特性结合tput的终端检测逻辑共同导致的,具体拆解如下:
命令替换的子进程环境特性
用$(...)执行命令时,Bash会创建子进程,这个子进程的标准输出(stdout)会被绑定到管道(用来把命令结果传回父进程),不再直接连接终端。tput cols的终端检测逻辑tput cols获取终端宽度的逻辑是:- 优先检查当前进程是否有连接到终端的文件描述符(stdout或stderr);
- 如果找到任意一个连接到终端的描述符,就从该终端读取实际宽度;
- 如果所有相关描述符都未连接到终端,就返回默认的80列。
对应测试场景的具体解释:
tput cols/tput cols 2>/dev/null/echo $(tput cols):这几种情况里,要么直接执行时stdout连接终端,要么命令替换里stderr仍继承父进程的终端(未被重定向),tput能找到终端,因此返回实际宽度;echo $(tput cols 2>/dev/null)/echo $(tput cols 2>/tmp/file.tmp)这类命令:命令替换的子进程stdout是管道,stderr又被重定向到非终端(/dev/null或普通文件),tput找不到任何连接终端的描述符,就 fallback 到默认的80列;echo $(tput cols 2>/dev/ttys002):把stderr重定向回终端设备文件,tput检测到stderr连接终端,就能读取到实际终端宽度。
额外说明:macOS自带的Bash 3.2是较老旧的版本,这个逻辑在新版Bash或其他Shell(比如Zsh)中可能存在差异,但核心根源还是
tput对终端文件描述符的检测规则。
内容的提问来源于stack exchange,提问作者Cartaya
相关产品推荐
相关产品推荐

