如何在脚本执行后有条件地清除其输出?
实现低依赖的脚本输出条件清除包装器
需求背景
我需要开发一个脚本包装器,让被包装的脚本在执行过程中实时输出状态信息,执行完成后根据返回码有条件清除其输出:
- 脚本执行成功(返回码0):清除该脚本产生的所有输出,仅保留之前终端的内容
- 脚本执行失败(返回码非0):保留所有输出,方便排查问题
典型场景比如docker build、npm install这类命令,执行时需要让用户看到进度,但成功后冗长的输出会占用终端空间,失败则需要保留错误信息。
现有方案的不足
用screen命令可以实现执行时看输出、结束后隐藏的效果:
screen docker build <ARGS>
但这种方式存在两个问题:
- 依赖
screen工具,无法在无该工具的环境使用 - 脚本执行失败时,
screen退出后无法保留错误输出,不符合需求
理想行为
运行my-wrapper <COMMAND>需满足:
- 启动时不清除终端屏幕(避免像多数curses程序那样清屏)
- 正确传递被包装命令的
stdout和stderr,保持实时输出 - 子进程终止后:
- 返回码0:仅清除该脚本产生的输出
- 返回码非0:保留所有输出(必要时重新打印)
Bash实现(无额外依赖)
利用终端ANSI控制码实现,完全依赖系统自带的Bash,无需额外安装工具:
#!/bin/bash # 仅在终端环境下执行清除逻辑 if [[ -t 1 ]]; then # 获取当前光标起始行位置 IFS=';' read -sdR -p $'\033[6n' row col start_row=$row # 执行传入的目标命令 "$@" exit_code=$? # 成功时清除输出 if [[ $exit_code -eq 0 ]]; then # 获取当前光标位置 IFS=';' read -sdR -p $'\033[6n' current_row col # 计算需要回退的行数 lines_up=$((current_row - start_row)) # 回到起始行并清除后续所有内容 echo -e "\033[${lines_up}A\033[J" fi exit $exit_code else # 非终端环境(如管道、重定向),直接执行命令 "$@" exit $? fi
使用说明
- 将脚本保存为
my-wrapper,赋予执行权限:chmod +x my-wrapper - 使用方式:
./my-wrapper docker build <ARGS>
Python实现(低依赖)
依赖系统自带的Python3(多数Linux/macOS系统预装),逻辑更健壮,兼容性更好:
#!/usr/bin/env python3 import os import sys import subprocess import fcntl import termios def get_cursor_position(): # 发送终端控制码请求光标位置 sys.stdout.write('\033[6n') sys.stdout.flush() # 读取终端响应并解析 buf = [] while True: char = sys.stdin.read(1) buf.append(char) if char == 'R': break response = ''.join(buf) row_part, col_part = response.split(';') row = int(row_part.split('[')[1]) col = int(col_part.split('R')[0]) return row, col def main(): if len(sys.argv) < 2: print(f"用法: {sys.argv[0]} <命令> [参数...]", file=sys.stderr) sys.exit(1) if sys.stdout.isatty(): try: start_row, _ = get_cursor_position() except Exception: # 获取光标位置失败,直接执行命令 subprocess.run(sys.argv[1:]) sys.exit() # 执行目标命令,实时输出 process = subprocess.Popen(sys.argv[1:], stdout=sys.stdout, stderr=sys.stderr) exit_code = process.wait() if exit_code == 0: try: current_row, _ = get_cursor_position() lines_up = current_row - start_row # 回退到起始行并清除后续内容 sys.stdout.write(f'\033[{lines_up}A\033[J') sys.stdout.flush() except Exception: # 清除失败不影响正常退出 pass sys.exit(exit_code) else: # 非终端环境直接执行命令 subprocess.run(sys.argv[1:]) if __name__ == '__main__': main()
使用说明
- 保存为
my-wrapper.py,赋予执行权限:chmod +x my-wrapper.py - 使用方式:
./my-wrapper.py npm install
注意事项
- 若执行命令过程中用户手动滚动终端,清除逻辑可能会出错(因为基于屏幕光标位置计算,而非实际输出行数)
- 仅支持ANSI控制码兼容的终端(现代主流终端如xterm、iTerm2、GNOME Terminal均支持)
- 非终端环境(如命令输出被重定向到文件/管道)下,包装器会直接执行命令,不进行清除操作
内容的提问来源于stack exchange,提问作者frans
相关产品推荐
相关产品推荐

