跳到主要内容
ansicode

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` 字面形式。

shell / env
# 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_md

LESS=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`,除非确实需要渲染其它控制字符。

shell / env
# 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' | head

MANPAGER 与 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_*` —— 透明地覆盖两条路径。

shell / env
# 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 text

LESSOPEN / 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"`(注意前导空格)保持颜色透传。

shell / env
# 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` 都尊重。

shell / env
# 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 UI

diff-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`。

shell / env
# 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 专属语法。

shell / env
# ~/.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, ~/.zshrc

LESS_TERMCAP_* 槽位参考

七个 LESS_TERMCAP_* 环境变量 —— 每个起或结束什么、配对的起 / 结束变量、less 赋予它的角色。me 重置被 md / mb / so 共享;ue 仅与 us 配对;se 仅与 so 配对。

LESS_TERMCAP_* 槽位参考
环境变量配对含义用途
LESS_TERMCAP_mdme起粗体man 页面关键字 / 命令名 / 标题
LESS_TERMCAP_memd/mb/so结束粗体 / 闪烁 / standout(单一重置)三个起始序列的共享重置
LESS_TERMCAP_usue起下划线(man 中为斜体)man 页面参数名 / 文件名 / 强调
LESS_TERMCAP_ueus结束下划线下划线起始序列的配对重置
LESS_TERMCAP_sose起 standout(反色高亮)less 底部的状态 / 提示行;搜索匹配高亮
LESS_TERMCAP_seso结束 standoutstandout 起始序列的配对重置
LESS_TERMCAP_mbme起闪烁(罕见)极少使用;从 md 复制以保持闪烁内容可见

本站覆盖周边 ANSI / pager / man / diff 生态的相邻页面。