refactor(core): ♻️ 优化终端宽度检测逻辑为动态获取

通过引入 terminal_width 函数,将原本固定的终端宽度检测逻辑重构为动态获取。当未指定固定宽度时,脚本将实时读取当前终端窗口尺寸,提升了在不同交互环境下(如窗口缩放)的显示适配能力。

同时更新了配套文档,明确了命令行参数、环境变量与动态检测之间的优先级关系。
This commit is contained in:
2026-05-04 02:02:23 +08:00
parent 3c980ec87a
commit 6a0fe9dded
2 changed files with 62 additions and 24 deletions

View File

@@ -42,7 +42,7 @@ chmod +x brew-upgrade-manager.sh
./brew-upgrade-manager.sh
```
定终端宽度:
默认会动态读取当前终端宽度;运行过程中缩放窗口时,`brew cu` 的 PTY 尺寸也会跟随更新。如果遇到非交互环境或某些表格渲染异常,可以指定固定终端宽度:
```bash
./brew-upgrade-manager.sh --width 130
@@ -55,7 +55,7 @@ chmod +x brew-upgrade-manager.sh
HB_TERMINAL_WIDTH=130 ./brew-upgrade-manager.sh
```
优先级为:命令行 `--width` 高于 `HB_TERMINAL_WIDTH`,再高于自动检测值
优先级为:命令行 `--width` 高于 `HB_TERMINAL_WIDTH`。两者都不设置时使用动态终端宽度
## sudo 认证
@@ -91,7 +91,7 @@ brew tap buo/cask-upgrade
### 表格渲染或 Ruby 报终端宽度错误
使用固定宽度运行:
默认会跟随终端窗口变化;如果某些环境无法正确报告窗口尺寸,可以使用固定宽度运行:
```bash
./brew-upgrade-manager.sh --width 130

View File

@@ -53,21 +53,33 @@ while [[ $# -gt 0 ]]; do
esac
done
# 确定最终的 TERMINAL_WIDTH
FIXED_TERMINAL_WIDTH=""
if [[ -n "$TERMINAL_WIDTH_OVERRIDE" ]]; then
TERMINAL_WIDTH="$TERMINAL_WIDTH_OVERRIDE"
FIXED_TERMINAL_WIDTH="$TERMINAL_WIDTH_OVERRIDE"
elif [[ -n "$HB_TERMINAL_WIDTH" && "$HB_TERMINAL_WIDTH" =~ ^[0-9]+$ ]]; then
TERMINAL_WIDTH="$HB_TERMINAL_WIDTH"
elif command -v stty &>/dev/null && stty size &>/dev/null; then
TERMINAL_WIDTH=$(stty size 2>/dev/null | awk '{print $2}')
if [[ -z "$TERMINAL_WIDTH" || "$TERMINAL_WIDTH" -le 0 ]]; then
TERMINAL_WIDTH=$(tput cols 2>/dev/null || echo "$DEFAULT_FALLBACK_WIDTH")
fi
else
TERMINAL_WIDTH=$(tput cols 2>/dev/null || echo "$DEFAULT_FALLBACK_WIDTH")
FIXED_TERMINAL_WIDTH="$HB_TERMINAL_WIDTH"
fi
separator() { printf '=%.0s' $(seq 1 "$TERMINAL_WIDTH"); printf "\n"; }
terminal_width() {
local width=""
if [[ -n "$FIXED_TERMINAL_WIDTH" ]]; then
printf "%s" "$FIXED_TERMINAL_WIDTH"
return
fi
if command -v stty &>/dev/null && stty size &>/dev/null; then
width=$(stty size 2>/dev/null | awk '{print $2}')
fi
if [[ -z "$width" || "$width" -le 0 ]]; then
width=$(tput cols 2>/dev/null || echo "$DEFAULT_FALLBACK_WIDTH")
fi
if [[ -z "$width" || "$width" -le 0 ]]; then
width="$DEFAULT_FALLBACK_WIDTH"
fi
printf "%s" "$width"
}
separator() { local width; width=$(terminal_width); printf '=%.0s' $(seq 1 "$width"); printf "\n"; }
print_header() { echo -e "${BLUE}$1${NC}"; }
# ================== 流程开始 ==================
@@ -113,13 +125,18 @@ echo -e "\n${CYAN}>>> [2/2] Upgrading GUI Casks (brew cu -yaq)...${NC}"
# 强制注入环境变量,确保终端输出依然保留 ANSI 颜色格式
export HOMEBREW_COLOR=1
# --- 核心修复:环境变量宽度兜底 ---
# 强制将 Bash 计算好的真实终端宽度传递给底层,防止 Ruby 绘表时发生 negative argument 崩溃
export COLUMNS="$TERMINAL_WIDTH"
# --- 终端宽度处理 ---
# 交互式终端里让子进程读取 PTY 的实时尺寸;非交互环境或固定宽度模式下提供 COLUMNS 兜底。
if [[ -n "$FIXED_TERMINAL_WIDTH" || ! -t 1 ]]; then
export COLUMNS
COLUMNS="$(terminal_width)"
else
unset COLUMNS
fi
# 使用 Python 原生 PTY 自建极简转发引擎。它只转发用户输入,不保存、不注入 sudo 密码。
python3 -c '
import pty, os, sys, select, fcntl, termios, struct, tty
import pty, os, sys, select, fcntl, termios, struct, tty, signal
cmd = sys.argv[1:]
pid, fd = pty.fork()
@@ -127,13 +144,31 @@ pid, fd = pty.fork()
if pid == 0:
os.execvp(cmd[0], cmd)
else:
try:
def sync_winsize(*_):
s = struct.pack("HHHH", 0, 0, 0, 0)
try:
winsize = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s)
rows, cols, xpix, ypix = struct.unpack("HHHH", winsize)
if rows > 0 and cols > 0:
fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize)
return
except Exception:
pass
try:
fallback_cols = int(os.environ.get("COLUMNS", "130") or "130")
except ValueError:
fallback_cols = 130
try:
fallback_rows = int(os.environ.get("LINES", "40") or "40")
except ValueError:
fallback_rows = 40
fallback = struct.pack("HHHH", fallback_rows, fallback_cols, 0, 0)
fcntl.ioctl(fd, termios.TIOCSWINSZ, fallback)
sync_winsize()
signal.signal(signal.SIGWINCH, sync_winsize)
stdin_fd = sys.stdin.fileno()
stdout_fd = sys.stdout.fileno()
forward_stdin = sys.stdin.isatty()
@@ -149,7 +184,10 @@ else:
watch = [fd]
if forward_stdin:
watch.append(stdin_fd)
try:
rfds, _, _ = select.select(watch, [], [], 0.1)
except InterruptedError:
continue
if fd in rfds:
data = os.read(fd, 8192)