From 6a0fe9dded4fa4eb43b530880453c747a4e5f477 Mon Sep 17 00:00:00 2001 From: Orion Date: Mon, 4 May 2026 02:02:23 +0800 Subject: [PATCH] =?UTF-8?q?refactor(core):=20=E2=99=BB=EF=B8=8F=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BB=88=E7=AB=AF=E5=AE=BD=E5=BA=A6=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E9=80=BB=E8=BE=91=E4=B8=BA=E5=8A=A8=E6=80=81=E8=8E=B7?= =?UTF-8?q?=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 通过引入 terminal_width 函数,将原本固定的终端宽度检测逻辑重构为动态获取。当未指定固定宽度时,脚本将实时读取当前终端窗口尺寸,提升了在不同交互环境下(如窗口缩放)的显示适配能力。 同时更新了配套文档,明确了命令行参数、环境变量与动态检测之间的优先级关系。 --- homebrew/README.md | 6 +-- homebrew/brew-upgrade-manager.sh | 80 +++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 24 deletions(-) diff --git a/homebrew/README.md b/homebrew/README.md index 242442d..74fa4f1 100644 --- a/homebrew/README.md +++ b/homebrew/README.md @@ -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 diff --git a/homebrew/brew-upgrade-manager.sh b/homebrew/brew-upgrade-manager.sh index 20a1917..44ba3f9 100644 --- a/homebrew/brew-upgrade-manager.sh +++ b/homebrew/brew-upgrade-manager.sh @@ -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,12 +144,30 @@ 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) - winsize = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, s) - fcntl.ioctl(fd, termios.TIOCSWINSZ, winsize) - except Exception: - pass + 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() @@ -149,7 +184,10 @@ else: watch = [fd] if forward_stdin: watch.append(stdin_fd) - rfds, _, _ = select.select(watch, [], [], 0.1) + try: + rfds, _, _ = select.select(watch, [], [], 0.1) + except InterruptedError: + continue if fd in rfds: data = os.read(fd, 8192)