LESS 颜色 —— 通过 LESS_TERMCAP_*、LESS=R、MANPAGER 给 man 页面着色
`less` 读取 `LESS_TERMCAP_*` 环境变量为它从 `groff` 重叠看到的粗体 / 下划线 / standout 序列着色 —— 这是给 `man` 页面着色的规范配方。另一半是 `LESS=R`(或 `LESS='-R -F -X'`),它让上游管道里(来自 `git log --color=always`、`ls --color=always`、`grep --color=always`)已有的 SGR 字节穿过 less 到达终端,而非被剥离。下方七段复制即用片段覆盖完整地图:规范的 6 变量 LESS_TERMCAP_* 集合、LESS 环境标志、MANPAGER + MANROFFOPT + GROFF_NO_SGR 互动、lesspipe / LESSOPEN 预处理、bat 作为着色 MANPAGER、diff-so-fancy / delta 作为增强的 git pager,以及按 shell 接线。
配置片段
LESS_TERMCAP_* —— 让 man 页面着色的权威 6 变量集
`less` 读取 `LESS_TERMCAP_*` 环境变量来**覆盖**它默认从 terminfo 取的粗体 / 下划线 / standout 序列。当 `man` 调用 `less` 分页输出时,`groff` 产出的 nroff 用退格-重叠(`b\bbo\bol\bld\bd` = 粗体)与下划线-重叠(`_\bb_\bo_\bl_\bd` = 斜体 / 参数名)—— `less` 解析这些并发出配置好的 `LESS_TERMCAP_*` 字节。六个变量重要:`md`(起粗体)、`me`(结束粗体 / 闪烁 / standout)、`us`(起下划线)、`ue`(结束下划线)、`so`(起 standout —— 用于状态 / 提示行与搜索匹配)、`se`(结束 standout)。可选 `mb`(起闪烁)通常复制自 `md`,避免闪烁内容被隐藏。值是**原始** ANSI / VT 转义字节 —— 在 shell 配置里直接写 `\e[01;31m` 字面形式。
# The canonical 6-var set — drop into ~/.bashrc / ~/.zshrc verbatim.
# Bold red for keywords / commands (md), green underline for arguments
# (us), yellow standout for status line + search matches (so).
export LESS_TERMCAP_md=$'\e[01;31m' # start bold (man: keywords)
export LESS_TERMCAP_me=$'\e[0m' # end bold / blink / standout
export LESS_TERMCAP_us=$'\e[01;32m' # start underline (man: args / files)
export LESS_TERMCAP_ue=$'\e[0m' # end underline
export LESS_TERMCAP_so=$'\e[01;44;33m' # start standout (status / search hit)
export LESS_TERMCAP_se=$'\e[0m' # end standout
export LESS_TERMCAP_mb=$'\e[01;31m' # start blink — copy md so it stays visible
# Verify they are exported with the right bytes (decode the ESC):
env | grep LESS_TERMCAP | cat -v
# should show ^[[01;31m etc — the ^[ is ESC (0x1b), the rest is the SGR
# See the effect immediately — load a man page:
man ls
# Quick a/b test — unset, reload, compare:
( unset LESS_TERMCAP_md LESS_TERMCAP_me LESS_TERMCAP_us \
LESS_TERMCAP_ue LESS_TERMCAP_so LESS_TERMCAP_se; \
man ls )
# vs the current shell session with the vars set.
# Note the $'...' syntax — bash / zsh ANSI-C quoting that turns \e into
# the literal ESC byte. If your shell does NOT support $'...' (older sh /
# dash) use printf:
# LESS_TERMCAP_md=$(printf '\e[01;31m'); export LESS_TERMCAP_mdLESS=R —— 上游直接发出原始 SGR 时的颜色透传
`LESS_TERMCAP_*` 覆盖 `less` 自己通过**解析**输入来加颜色的情形。另一种情形是上游**已经发出 SGR 字节**(如 `git log --color=always`、`ls --color=always`、`grep --color=always`、`colordiff`、`diff-so-fancy`)—— 这些字节需要透传 `less` 到终端。默认 `less` 会剥离控制字符并显示 `^[[31m` 字面噪音。修复是 `-R` 标志(或 `--RAW-CONTROL-CHARS`),它让 SGR 字节通过同时仍清理其它控制字符。`LESS` 环境变量设置每次 `less` 调用默认应用的标志 —— `LESS=R` 让 raw-control-chars 成为全局默认。现代 `less`(v530 起,2017)也支持 `-r`(小写)传递**所有**控制字符 —— 用 `-R` 而非 `-r`,除非确实需要渲染其它控制字符。
# Set the default once — every less invocation gains -R:
export LESS=R
# Optional companion flags many users add to LESS:
export LESS='-R -F -X'
# -R keep SGR bytes (already covered above)
# -F quit if output fits one screen (skip the pager-trap on short files)
# -X skip the alt-screen restore (so scrollback retains the pager content)
# These are the same flags git auto-sets when invoking its DEFAULT pager.
# Custom pagers (core.pager, PAGER, MANPAGER) do NOT inherit them — set
# LESS globally instead.
# One-shot — no env, flags on the command line:
git log --color=always --oneline | less -R
grep --color=always -rn TODO src/ | less -R
ls --color=always -la | less -R
# Without -R (the default), you see literal noise:
ls --color=always | less
# src/ app/ ESC[01;34mlib^[[0mESC[m ... (ugly)
# With -R, colour passes through:
ls --color=always | less -R
# src/ app/ (bold blue lib) ... (correct)
# Verify LESS is honoured by the binary:
echo "$LESS"
less --help 2>&1 | grep -E 'RAW|environment' | headMANPAGER 与 PAGER —— 现代 man 两者皆读,但 MANPAGER 胜出
`man` 按此顺序查找 pager:`$MANPAGER`、`$PAGER`、编译期默认(通常 `less`)。设置 `MANPAGER` 让你给 `man` 一个与日常 `less` 不同的 pager —— 在希望 `man` 始终强制颜色渲染但保持普通 `less` 调用不变时很有用。规范安全值是 `MANPAGER='less -R'`。在 `groff` 足够新(1.18.1+,2008)且设置 `MANROFFOPT=-c` 的发行版上,`groff` 直接发出 SGR 而非退格-重叠 —— 此时 `LESS_TERMCAP_*` 失效(less 不再解析重叠),需要 `MANPAGER='less -R'` 才能让 SGR 通过。现代 Debian / Ubuntu / Arch 默认就是这个配置;BSD 与较旧 RHEL 仍走重叠路径。两者都设 —— `MANPAGER='less -R'` 与 `LESS_TERMCAP_*` —— 透明地覆盖两条路径。
# Set both — covers groff-SGR path AND backspace-overstrike path:
export MANPAGER='less -R'
export MANROFFOPT='-c' # tell groff to emit SGR (modern groff)
# Plus the LESS_TERMCAP_* vars from snippet 1 (for the overstrike path).
# Check which path your distro uses — run man on any page, then in less
# press 'v' to drop into $EDITOR (or use !cat) and look at the raw bytes:
#
# If you see "b\bbo\bol\bld" overstrike → backspace-overstrike path,
# LESS_TERMCAP_* is in play.
# If you see "\x1b[1mbold\x1b[0m" → groff-SGR path,
# MANPAGER='less -R' is what matters.
# Force the overstrike path even on a modern distro (so LESS_TERMCAP_*
# applies again — useful when debugging custom palettes):
export GROFF_NO_SGR=1
# Test:
man ls
# Try changing LESS_TERMCAP_md to a glaring colour and reload to confirm
# which path is active:
export LESS_TERMCAP_md=$'\e[01;45m' # purple background
man ls # if you see purple text → overstrike path; else → SGR path
# When piping man output (e.g. into grep / a file) the formatting is
# normally stripped because the pager is not a TTY. Preserve it with:
MAN_KEEP_FORMATTING=1 man ls | head -50 # keeps overstrike bytes
# Then strip later with col -b:
MAN_KEEP_FORMATTING=1 man ls | col -b | head -50 # plain textLESSOPEN / lesspipe —— 在 less 看到输入前预处理
`less` 对每个输入文件运行 `$LESSOPEN`(可选 `$LESSCLOSE`),让用户在字节抵达 pager 前有机会变换 —— 多数发行版自带 `lesspipe.sh`(Debian / Ubuntu)或 `lesspipe.lua`(Arch),通过 `/etc/profile.d/less.sh` 中的 `eval "$(lesspipe)"` 接线。开箱即用让 `less archive.tar.gz` 显示目录列表、`less foo.pdf` 显示提取文本、`less foo.deb` 显示包元数据 —— 比单独 `less` 的输入支持广得多。颜色角度:`lesspipe` 与 `source-highlight`(或 `highlight`、`bat`、`pygmentize`)集成,使 `less foo.py` 显示带语法高亮的 Python 源代码。通过 `LESSOPEN='| /usr/bin/src-hilite-lesspipe.sh %s'` 配置高亮器,并添加 `LESS=" -R"`(注意前导空格)保持颜色透传。
# Default Debian / Ubuntu setup (already in /etc/profile.d/less.sh):
export LESSOPEN="| /usr/bin/lesspipe %s"
export LESSCLOSE="/usr/bin/lesspipe %s %s"
# Verify it's working — should show metadata not raw bytes:
less /usr/lib/x86_64-linux-gnu/libc.so.6 # ELF binary → strings dump
less /etc/os-release # text file → straight through
less ./some.tar.gz # archive → tar tvfz listing
# Add syntax highlighting for source code via source-highlight:
sudo apt install source-highlight # Debian / Ubuntu
brew install source-highlight # macOS
# Wire it as the pre-processor — note leading space in LESS:
export LESSOPEN="| /usr/bin/src-hilite-lesspipe.sh %s"
export LESS=" -R" # leading space prevents typoed
# binary name issues
# Test it:
less app/page.tsx # should show syntax-highlighted TS
less script.sh # should show syntax-highlighted bash
less /etc/nginx/nginx.conf # should show coloured nginx config
# Disable for one invocation (debugging):
LESSOPEN= less raw.log
less --no-lessopen raw.log # explicit flag, less v530+
# Combine with bat for richer highlighting (snippet 5 explores bat more):
export LESSOPEN="| /usr/bin/bat --color=always --paging=never %s"bat / batcat —— 带语法高亮的着色 less 替代
`bat`(作者 sharkdp —— Rust)是 `cat` / `less` 混合体,增加语法高亮、行号、Git 修改标记、与遵循 `less` 键位的分页模式(`bat -p` 不分页)。它既是 `cat` 的直接替代(`alias cat=bat`),也可作为 MANPAGER 提供带语法高亮代码块的着色 man 页面。Debian / Ubuntu 上二进制重命名为 `batcat`(更早的无关 `bat` 包占用了名字)—— Arch / Fedora / Homebrew 仍以 `bat` 安装。通过 `bat --list-themes` 查主题(`OneHalfDark` / `Dracula` / `gruvbox-dark` / `Solarized (light)` / `ansi` 仅终端调色板)。`BAT_THEME` 环境变量设默认;`BAT_PAGER='less -R'` 覆盖 pager。`--color={auto,always,never}` 与 `NO_COLOR` 都尊重。
# Install:
# Arch: pacman -S bat
# Debian/Ubuntu: apt install bat (binary is "batcat", not "bat")
# Fedora: dnf install bat
# macOS: brew install bat
# Debian / Ubuntu workaround — alias batcat back to bat:
alias bat='batcat'
# Use as a cat replacement (paging only when output exceeds terminal):
bat app/page.tsx # syntax-highlighted, line numbers
bat -p app/page.tsx # plain mode (no header, no pager)
bat -A binary.dat # show non-printable as escapes
# Use as MANPAGER for colourised man pages:
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
# col -bx strip backspace-overstrike (otherwise bat sees garbage)
# bat -l man syntax = man (highlights options + section refs)
# bat -p plain mode (no header)
# Theme + style configuration:
export BAT_THEME='OneHalfDark' # any theme from --list-themes
export BAT_STYLE='numbers,changes,header' # opt-in column set
export BAT_PAGER='less -R' # override default pager
# Verify which binary, where the config file lives:
bat --version
bat --config-file # path; create + edit to persist
bat --generate-config-file # write a fully-commented template
# Pipe coloured input into bat (preserve SGR):
git log --color=always --oneline | bat --paging=always
diff -u old.txt new.txt | bat -l diff
# Use as drop-in cat (with caveats — bat exits with paging on long output):
alias cat='bat -p' # plain mode = no extra UIdiff-so-fancy / delta —— 增强的着色 diff pager
两个针对 git / diff 输出的流行 pager 替代,都假设 `LESS=" -R"`(或 `LESS=FRX`)已设。**`diff-so-fancy`**(作者 so-fancy —— Perl)清理统一 diff 输出:移除 `+++ / ---` 标头、用细分隔符替换 `@@` hunk 标记、用更亮 SGR 重新着色 `+ / -` 行。通过 `git config --global core.pager "diff-so-fancy | less --tabs=4 -RFX"` 接线。**`delta`**(作者 dandavison —— Rust)更进一步:并排模式、语法高亮代码、可配置行号 + 加减标记、Git-blame 集成。通过 `git config --global core.pager delta` 接线(delta 自动检测自己是 pager 并应用)。`delta` 从 `~/.gitconfig` `[delta]` 节读配置 —— 设 `features = side-by-side line-numbers decorations` 走现代外观。两者都尊重 `NO_COLOR` 与 `--color=never`。
# Install:
# diff-so-fancy: brew install diff-so-fancy (or npm i -g diff-so-fancy)
# delta: brew install git-delta (or cargo install git-delta)
# diff-so-fancy — wire into git core.pager:
git config --global core.pager 'diff-so-fancy | less --tabs=4 -RFX'
git config --global interactive.diffFilter 'diff-so-fancy --patch'
# Optional colour tuning that diff-so-fancy assumes:
git config --global color.diff-highlight.oldNormal 'red bold'
git config --global color.diff-highlight.oldHighlight 'red bold 52'
git config --global color.diff-highlight.newNormal 'green bold'
git config --global color.diff-highlight.newHighlight 'green bold 22'
git config --global color.diff.meta 'yellow'
git config --global color.diff.frag 'magenta bold'
git config --global color.diff.commit 'yellow bold'
git config --global color.diff.old 'red bold'
git config --global color.diff.new 'green bold'
git config --global color.diff.whitespace 'red reverse'
# delta — wire it as the pager (auto-detects):
git config --global core.pager delta
git config --global interactive.diffFilter 'delta --color-only'
git config --global delta.navigate true
git config --global delta.light false
git config --global delta.line-numbers true
git config --global delta.side-by-side true
git config --global delta.syntax-theme 'OneHalfDark'
# Disable for one command:
git --no-pager diff
git -c core.pager=cat diff
# Test:
git diff HEAD~1
git log --oneline -p | head -50
# Pipe a raw diff (not via git) into either tool:
diff -u old.txt new.txt | diff-so-fancy | less -R
diff -u old.txt new.txt | delta按 shell 接线 —— bash · zsh · fish · csh
权威接线把上述片段全部组合:`LESS_TERMCAP_*` 给 man 着色、`LESS='-R -F -X'` 走 SGR 透传 + 合理退出 + 保留 scrollback、`MANPAGER='less -R'` 让现代 groff 的 SGR 流过、可选 `BAT_THEME` 走语法高亮代码。每个 shell 导出方式不同。`/ls-colors` 与 `/grep-colors` 的 macOS bash 双坑同样适用 —— `~/.bashrc` 仅在非登录 shell 运行,但 Terminal.app 启动登录 shell,故 env 导出请放 `~/.bash_profile`(macOS 默认 zsh 时放 `~/.zprofile`)。Fish 需要通过 printf-into-set 模式拿到字面 ESC 字节,因为 `$'\e'` ANSI-C 引用是 bash / zsh 专属语法。
# ~/.bashrc (Linux) or ~/.bash_profile (macOS Terminal.app):
export LESS_TERMCAP_md=$'\e[01;31m' # bold → bold red
export LESS_TERMCAP_me=$'\e[0m'
export LESS_TERMCAP_us=$'\e[01;32m' # underline → bold green
export LESS_TERMCAP_ue=$'\e[0m'
export LESS_TERMCAP_so=$'\e[01;44;33m' # standout → bold yellow-on-blue
export LESS_TERMCAP_se=$'\e[0m'
export LESS_TERMCAP_mb=$'\e[01;31m'
export LESS='-R -F -X'
export MANPAGER='less -R'
export MANROFFOPT='-c'
export PAGER='less'
# ~/.zshrc / ~/.zprofile (same syntax, $'...' works in zsh too):
export LESS_TERMCAP_md=$'\e[01;31m'
# ... (same six exports)
export LESS='-R -F -X'
export MANPAGER='less -R'
# ~/.config/fish/config.fish — fish has NO $'...' quoting, use printf:
set -gx LESS_TERMCAP_md (printf '\e[01;31m')
set -gx LESS_TERMCAP_me (printf '\e[0m')
set -gx LESS_TERMCAP_us (printf '\e[01;32m')
set -gx LESS_TERMCAP_ue (printf '\e[0m')
set -gx LESS_TERMCAP_so (printf '\e[01;44;33m')
set -gx LESS_TERMCAP_se (printf '\e[0m')
set -gx LESS_TERMCAP_mb (printf '\e[01;31m')
set -gx LESS '-R -F -X'
set -gx MANPAGER 'less -R'
# ~/.cshrc / ~/.tcshrc — printf inside backticks, setenv (no = sign):
setenv LESS_TERMCAP_md "`printf '\e[01;31m'`"
setenv LESS_TERMCAP_me "`printf '\e[0m'`"
# ... (same six setenv)
setenv LESS '-R -F -X'
setenv MANPAGER 'less -R'
# Verify:
man ls # should show coloured keywords + underlined args
echo "$LESS" # should print "-R -F -X"
env | grep -E 'LESS_TERMCAP|MANPAGER' | cat -v # show with ^[ visible
# macOS double-trap reminder — these only run on LOGIN shell:
# ~/.bash_profile, ~/.zprofile, ~/.profile
# These only run on NON-login (subshell) shell:
# ~/.bashrc, ~/.zshrcLESS_TERMCAP_* 槽位参考
七个 LESS_TERMCAP_* 环境变量 —— 每个起或结束什么、配对的起 / 结束变量、less 赋予它的角色。me 重置被 md / mb / so 共享;ue 仅与 us 配对;se 仅与 so 配对。
| 环境变量 | 配对 | 含义 | 用途 |
|---|---|---|---|
| LESS_TERMCAP_md | me | 起粗体 | man 页面关键字 / 命令名 / 标题 |
| LESS_TERMCAP_me | md/mb/so | 结束粗体 / 闪烁 / standout(单一重置) | 三个起始序列的共享重置 |
| LESS_TERMCAP_us | ue | 起下划线(man 中为斜体) | man 页面参数名 / 文件名 / 强调 |
| LESS_TERMCAP_ue | us | 结束下划线 | 下划线起始序列的配对重置 |
| LESS_TERMCAP_so | se | 起 standout(反色高亮) | less 底部的状态 / 提示行;搜索匹配高亮 |
| LESS_TERMCAP_se | so | 结束 standout | standout 起始序列的配对重置 |
| LESS_TERMCAP_mb | me | 起闪烁(罕见) | 极少使用;从 md 复制以保持闪烁内容可见 |
相关参考
本站覆盖周边 ANSI / pager / man / diff 生态的相邻页面。
- Git 颜色配置配套按工具页面 —— git 的 color.ui / 按命令的键 / 槽位调色板 / core.pager + LESS=FRX(本页覆盖的同一 LESS 环境)。
- LS_COLORS 环境变量配套按工具页面 —— LS_COLORS 语法、dircolors 工作流、BSD LSCOLORS 分歧、eza / exa。
- GREP_COLORS 环境变量配套按工具页面 —— GREP_COLORS 槽位语法、ripgrep --colors 点号语法、ag --color-* 标志、管道再过 grep 的陷阱。
- ANSI 常见坑点相邻问题的「症状 → 成因 → 修复」—— less 默认剥离 SGR、MANPAGER 未被尊重、groff-SGR 与重叠路径混淆。