LS_COLORS — ANSI colours for ls, dircolors, file listings
GNU ls reads the LS_COLORS environment variable on every invocation — a colon-separated string of type=SGR and *.ext=SGR pairs that colours each entry by file type and suffix. BSD-derived ls (macOS, FreeBSD, OpenBSD) reads a totally different env var LSCOLORS with a fixed-position 22-character format and no extension support. Seven copy-paste snippets below cover the full LS_COLORS map: env-var syntax, dircolors workflow, the BSD divergence, the GNU canonical default, eza / exa extensions, --color / NO_COLOR / CLICOLOR overrides, and per-shell wire-up for bash / zsh / fish / csh.
Configuration snippets
LS_COLORS — the colon-separated key=value string syntax
GNU ls --color=auto reads the LS_COLORS environment variable on every invocation. It is a single colon-separated string of key=SGR pairs. Keys come in two flavours: type tokens (two-letter mnemonics matching file types — di directory, ln symlink, ex executable, fi regular file, pi pipe / FIFO, so socket, bd block device, cd character device, or orphaned symlink, mi missing target, tw writable sticky directory, ow writable other directory, st sticky directory, su setuid, sg setgid, ca capability, mh multi-hardlink, do Solaris door) and extension tokens (*.ext=SGR patterns that match by trailing filename suffix, case-sensitive by default — match-by-glob is NOT supported, only exact suffix). Values are SGR-parameter strings without the leading \x1b[ or trailing m: 01;31 = bold red foreground, 38;5;208 = 256-palette orange, 38;2;255;128;0 = 24-bit truecolor. Tokens are checked left-to-right at first hit; later entries override earlier ones.
# Minimal LS_COLORS — a few type tokens + a few extension tokens:
export LS_COLORS='di=01;34:ln=01;36:ex=01;32:*.tar=01;31:*.zip=01;31:*.gz=01;31:*.md=00;33'
# What each pair means:
# di=01;34 directories → bold blue
# ln=01;36 symlinks → bold cyan
# ex=01;32 executables → bold green
# *.tar=01;31 .tar files → bold red
# *.zip=01;31 .zip files → bold red
# *.gz=01;31 .gz files → bold red
# *.md=00;33 .md files → yellow
# 256-palette + truecolor (coreutils 8.32+, 2020):
export LS_COLORS='di=38;5;33:*.json=38;2;255;128;0'
# di=38;5;33 directories → palette index 33 (blue)
# *.json=38;2;… .json files → orange 24-bit
# Important: case-sensitive by default. Match .JPG separately from .jpg
# unless you set TZ-style insensitivity via dircolors --print-database flags.
export LS_COLORS='*.jpg=01;35:*.JPG=01;35:*.png=01;35:*.PNG=01;35'
# Verify the env var is set, paged for readability:
echo "$LS_COLORS" | tr ':' '\n'dircolors — turn a human-readable database into LS_COLORS
Hand-writing one giant colon-separated string is painful. dircolors consumes a multi-line, comment-friendly database (one key per line, # comments, section headers) and emits the LS_COLORS=…; export LS_COLORS shell command. Most distros ship /etc/DIR_COLORS (read-only) plus a user-editable ~/.dir_colors. dircolors -p prints the built-in default to stdout — pipe that into a file, edit it, then eval $(dircolors -b ~/.dir_colors) from your .bashrc. The output format is shell-aware: -b emits bash / sh syntax, -c emits csh / tcsh syntax. Fish users need a different wire-up — see snippet 7.
# 1. Generate a template — built-in default, fully commented:
dircolors -p > ~/.dir_colors
# Sample contents look like:
#
# # Files of special type
# DIR 01;34 # directory
# LINK 01;36 # symbolic link (a 'target' here for ls -l)
# EXEC 01;32 # files with execute permission
#
# # Archives or compressed (bright red)
# .tar 01;31
# .tgz 01;31
# .zip 01;31
#
# # Image formats (bright magenta)
# .jpg 01;35
# .png 01;35
# 2. Edit ~/.dir_colors to taste, then wire it into ~/.bashrc:
echo 'eval "$(dircolors -b ~/.dir_colors)"' >> ~/.bashrc
# 3. Reload the shell or:
eval "$(dircolors -b ~/.dir_colors)"
# Verify it landed:
ls --color=always | head
# csh / tcsh form:
eval \`dircolors -c ~/.dir_colors\`BSD ls — LSCOLORS is a TOTALLY different format (page's #1 differentiator)
macOS, FreeBSD, OpenBSD, NetBSD ship the BSD-derived ls — which does NOT read LS_COLORS. It reads LSCOLORS (note: no underscore) and the format is a fixed-position 22-character string where each pair of characters encodes (foreground, background) for a specific file type slot in a hard-coded order: directory · symlink · socket · pipe · executable · block-device · char-device · setuid · setgid · sticky-other-writable · other-writable. Each character is one of a-h (lowercase = colour) / A-H (uppercase = bold colour) / x (default). Letters map: a=black b=red c=green d=brown / yellow e=blue f=magenta g=cyan h=light-gray. There is NO extension-by-file-type support — BSD ls colours by type only, never by .tar / .zip suffix. Toggle colour with ls -G (BSD default off) or CLICOLOR=1 in env; force on pipe with CLICOLOR_FORCE=1. Most cross-platform dotfiles set BOTH LS_COLORS and LSCOLORS so the same ~/.bashrc works on Linux and macOS.
# BSD LSCOLORS — 11 pairs (22 chars), hard-coded slot order:
#
# pos slot default
# 1-2 directory ex (blue, default-bg)
# 3-4 symlink fx (magenta, default-bg)
# 5-6 socket cx (green, default-bg)
# 7-8 pipe / FIFO dx (brown / yellow, default-bg)
# 9-10 executable bx (red, default-bg)
# 11-12 block device egcx -- wait, this is wrong;
# the canonical slot is:
# block device cd (green, brown)
# 13-14 char device ed (blue, brown)
# 15-16 setuid ab (black, red)
# 17-18 setgid ag (black, cyan)
# 19-20 sticky+other-writable hb (light-gray, red)
# 21-22 other-writable hc (light-gray, green)
#
# Letter map (BSD-specific, NOT ANSI / SGR numerics):
# a=black b=red c=green d=brown e=blue f=magenta g=cyan h=light-gray
# uppercase = bold version of the same colour
# x = default terminal colour
#
# BSD ls colour default — the value of LSCOLORS if you don't set it:
export LSCOLORS='exfxcxdxbxegedabagacad'
# Enable colour on BSD ls — default-off, opposite of GNU ls:
export CLICOLOR=1 # honour TTY detection
export CLICOLOR_FORCE=1 # force on pipe (skip if you pipe to grep)
# Verify with ls -G (BSD-flag) or just ls (when CLICOLOR is set):
ls -G
# Cross-platform dotfile — set BOTH so Linux + macOS work from the same file:
export LS_COLORS='di=01;34:ln=01;36:ex=01;32:*.tar=01;31:*.zip=01;31'
export LSCOLORS='ExFxCxDxBxegedabagacad' # uppercase = bold on BSD
# Or just install GNU coreutils on macOS for unified behaviour:
# brew install coreutils
# alias ls='gls --color=auto'
# Then LS_COLORS applies on macOS too.The canonical GNU LS_COLORS default — copy-pasteable
If you just want the default that dircolors -p | dircolors --bourne-shell would produce on a stock GNU coreutils 9.x installation, here it is in one line. Save it to .bashrc and you have the upstream defaults without running dircolors at all — useful for minimal containers / Alpine / scratch images / distroless contexts where dircolors may not be installed. Type tokens come first, then a long tail of extension tokens grouped by colour (archives in red, images in magenta, audio in cyan, source code in default). Last writer wins, so put narrow extension overrides AFTER broad type tokens.
# Canonical GNU coreutils 9.x default LS_COLORS, one line:
export LS_COLORS='rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:'
# Notes on the special type tokens at the front:
# rs=0 reset (delimits coloured regions in a row)
# mh=00 multi-hardlink — empty value = use default
# pi=40;33 pipe / FIFO → black bg, yellow fg
# so=01;35 socket → bold magenta
# do=01;35 Solaris door → bold magenta
# bd=40;33;01 block dev → black bg, bold yellow fg
# cd=40;33;01 char dev → black bg, bold yellow fg
# or=40;31;01 orphan link → black bg, bold red fg
# mi=00 missing target — empty = use default
# su=37;41 setuid → white fg, red bg
# sg=30;43 setgid → black fg, yellow bg
# ca=00 capability — empty = use default (capabilities deprecated)
# tw=30;42 sticky + other-writable → black fg, green bg
# ow=34;42 other-writable → blue fg, green bg
# st=37;44 sticky → white fg, blue bg
# ex=01;32 executable → bold greeneza / exa — modern ls replacements with their own colour systems
exa (deprecated, last commit 2022) and its actively-maintained fork eza are popular ls replacements. They honour LS_COLORS for backward compatibility BUT also support their own richer per-element colour env var EXA_COLORS / EZA_COLORS with extra slots GNU ls lacks: file permissions (ur user-read, uw user-write, ux user-execute, plus gr gw gx tr tw tx group / other), inode columns (in), size unit suffixes (sn size-number, sb size-unit-byte), date columns (da accent date, df future-date, etc.), and git status columns (ga added, gm modified, gd deleted, gn renamed, gv updated). Same key=SGR syntax. Set EZA_COLORS=reset first to drop the defaults if you want a pure custom palette.
# Minimal eza override — keep LS_COLORS, add eza-specific slots:
export EZA_COLORS='ur=33:uw=31:ux=32:gr=33:gw=31:gx=32:in=38;5;240:sn=38;5;33:sb=38;5;240'
# What each pair means in eza output:
# ur=33 user read → yellow
# uw=31 user write → red
# ux=32 user execute → green
# gr=33 group read → yellow
# gw=31 group write → red
# gx=32 group execute → green
# in=38;5;240 inode column → 256-palette dim grey
# sn=38;5;33 size number → palette blue
# sb=38;5;240 size byte unit → palette dim grey
# eza git-status column slots (only when --git is passed):
export EZA_COLORS='ga=32:gm=33:gd=31:gn=35:gv=36:ga|gm|gd|gn|gv=01'
# ga=32 added → green
# gm=33 modified → yellow
# gd=31 deleted → red
# gn=35 renamed → magenta
# gv=36 updated → cyan
# Reset eza defaults and start from a clean slate:
export EZA_COLORS='reset:ur=33:uw=31:ux=32'
# Same syntax for the older exa binary:
export EXA_COLORS='reset:ur=33:uw=31:ux=32'
# Aliases for ls drop-in:
alias ls='eza --color=auto'
alias ll='eza -l --git --color=auto'
alias la='eza -la --color=auto'
alias tree='eza --tree --color=auto'--color flag · NO_COLOR · CLICOLOR · CLICOLOR_FORCE — per-invocation overrides
GNU ls accepts --color={auto,always,never}. auto (the default since most distros' LS_COLORS setup) emits SGR only on TTY; always forces colour even into pipes (useful with less -R); never disables. The universal NO_COLOR env var (since coreutils 8.30 / 2018) overrides everything — sets --color=never effectively. BSD ls uses different flags: -G enables colour (default off), CLICOLOR=1 env equivalent, CLICOLOR_FORCE=1 forces colour through pipes. eza / exa mirror GNU's --color flag and honour NO_COLOR. Tip: use --color=always only when piping into a renderer (less -R, bat, aha) — piping into grep / wc with colour active inserts SGR into the match string and breaks naive substring matching.
# GNU ls — three colour modes:
ls --color=auto # TTY-only (the sensible default)
ls --color=always # force colour through pipes
ls --color=never # disable colour entirely
NO_COLOR=1 ls # universal env opt-out (coreutils 8.30+)
# Pipe coloured listing into a pager that renders SGR:
ls --color=always -la | less -R
# BSD ls — different flag set:
ls -G # one-shot enable (BSD-only flag)
CLICOLOR=1 ls # env equivalent
CLICOLOR_FORCE=1 ls | grep .conf # force through pipe (BSD)
# eza / exa equivalents:
eza --color=always -la | bat
NO_COLOR=1 eza # honoured
# Defeat parent shell's alias for one invocation:
\\ls # bypasses alias, runs the binary directly
command ls --color=never
# Common trap — colour bytes break grep substring matching:
ls --color=always | grep ".conf" # wrong — may miss matches
ls --color=never | grep ".conf" # right — clean bytes
ls --color=always | grep --color=never ".conf" # right — strip in grep
# Verify NO_COLOR is honoured site-wide (coreutils, grep, ripgrep, etc.):
env | grep -i color
NO_COLOR=1 ls && NO_COLOR=1 grep '' file.txt && NO_COLOR=1 rg patternPer-shell wire-up — bash · zsh · fish · csh
LS_COLORS is a plain env var; any shell that exports env vars can set it. The wire-up differs per shell. bash and zsh share the eval $(dircolors -b) pattern. fish is friendlier — set -gx LS_COLORS … works directly, and fish ships its own per-element colours configurable via set fish_color_*. csh / tcsh need dircolors -c (csh-syntax output) and setenv. macOS bash users hit the dual-trap: their ~/.bashrc only runs on non-login shells, but Terminal.app spawns login shells — put env exports in ~/.bash_profile (or ~/.zprofile for the macOS default zsh).
# ~/.bashrc (Linux) or ~/.bash_profile (macOS Terminal.app):
if [ -r ~/.dir_colors ]; then
eval "$(dircolors -b ~/.dir_colors)"
else
eval "$(dircolors -b)"
fi
alias ls='ls --color=auto'
# ~/.zshrc / ~/.zprofile (macOS default since 10.15):
if (( $+commands[dircolors] )); then
eval "$(dircolors -b ~/.dir_colors 2>/dev/null || dircolors -b)"
fi
alias ls='ls --color=auto'
# ~/.config/fish/config.fish:
if command -q dircolors
eval (dircolors -c ~/.dir_colors 2>/dev/null | sed 's/setenv/set -gx/')
end
alias ls 'ls --color=auto'
# Or use fish's native per-element colours instead of LS_COLORS:
set -gx fish_color_command green
set -gx fish_color_param normal
set -gx fish_color_quote yellow
set -gx fish_color_error red --bold
set -gx fish_color_autosuggestion 8a8a8a
# ~/.cshrc / ~/.tcshrc:
eval \`dircolors -c ~/.dir_colors\`
alias ls 'ls --color=auto'
# macOS double-trap reminder — these only run on LOGIN shell:
# ~/.bash_profile, ~/.zprofile, ~/.profile
# These only run on NON-login (subshell) shell:
# ~/.bashrc, ~/.zshrc
# Terminal.app spawns login shells; iTerm2 + VS Code terminal default
# to non-login. Source one from the other for symmetry:
# echo '[ -r ~/.bashrc ] && . ~/.bashrc' >> ~/.bash_profileRelated references
Adjacent pages on this site that cover the surrounding ANSI / ls / pager ecosystem.
- Git colour configCompanion per-tool page — color.ui / per-command keys / the slot palette / core.pager + LESS=FRX / --color=always pipe recipes for git.
- Strip ANSI codesAfter-the-fact regex to remove SGR bytes from a captured
ls --color=alwaysoutput — useful when redirecting to a file for archival. - Common ANSI pitfallsSymptom → cause → fix for adjacent issues — colour bytes break grep substring matching, GNU vs BSD ls divergence, NO_COLOR not honoured.
- Accessibility & NO_COLORDeeper coverage of the NO_COLOR / CLICOLOR / FORCE_COLOR env-var contract across non-CI tools — how screen readers handle escape-laden file listings.