refactor(core): ♻️ 优化终端宽度检测逻辑为动态获取
通过引入 terminal_width 函数,将原本固定的终端宽度检测逻辑重构为动态获取。当未指定固定宽度时,脚本将实时读取当前终端窗口尺寸,提升了在不同交互环境下(如窗口缩放)的显示适配能力。 同时更新了配套文档,明确了命令行参数、环境变量与动态检测之间的优先级关系。
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user