Changing the color output of tree
Changing the color output of tree
First problem to document this: how do you even capture the color output of the terminal?
Googled [copy terminal output with color].
First result: https://www.linuxquestions.org/questions/linux-desktop-74/preserve-colors-when-copy-pasting-from-terminal-943213/
Recommended package in Ubuntu repo: aha (ANSI HTML Adapter).
$ apt show aha
Package: aha
Version: 0.5-1
Priority: extra
Section: universe/utils
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Axel Beckert <abe@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 48.1 kB
Depends: libc6 (>= 2.4)
Homepage: https://github.com/theZiz/aha
Download-Size: 13.2 kB
APT-Sources: http://es.archive.ubuntu.com/ubuntu focal/universe amd64 Packages
Description: ANSI color to HTML converter
aha (ANSI HTML Adapter) converts ANSI colors to HTML, e.g. if you
want to publish the output of ls --color=yes, git diff, ccal or htop
as static HTML somewhere.
Install aha with apt. No surprises here.
Pipe the output of tree, forcing color with -C
, to aha.
tree -C /media/isme/Samsung_T5/backup/ | aha
The output is complete HTML document. I can copy and paste the <pre>
tag
literally into a markdown document to capture the color output of my terminal.
/media/isme/Samsung_T5/backup/ ├── config ├── data │ └── 0 │ ├── 0 │ └── 1 ├── hints.1 ├── index.1 ├── integrity.1 ├── nonce └── README 2 directories, 8 files
Now, you can see, dark blue on dark green is unreadable.
But tree renders normal folders with blue text on a transparent background. What kind of file is the “backup” folder?
ls -ld /media/isme/Samsung_T5/backup/ ~
drwxr-xr-x 102 isme isme 4096 Apr 24 11:31 /home/isme drwxrwxrwx 1 isme isme 131072 Apr 23 21:06 /media/isme/Samsung_T5/backup/
The permissions show that the folder is world-writable. Does that make a difference?
And in any case, how do I change it?
From man tree
:
Tree is a recursive directory listing program that produces a depth indented listing of files, which is colorized ala dircolors if the LS_COLORS environment variable is set and output is to tty.
I’m not sure what output is to tty
refers to exactly. When tree is the only
command in the pipeline, it outputs color to the terminal. When tree is piped
into something else, it outputs color only with the -C
option.
My .bashrc refers also to dircolors.
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
From man ls
:
Using color to distinguish file types is disabled both by default and with
--color=never
. With--color=auto
, ls emits color codes only when standard output is connected to a terminal. The LS_COLORS environment variable can change the settings. Use the dircolors command to set it.
LS_COLORS is set. But where is it set?
$ test -n "$LS_COLORS" && echo "LS_COLORS is set" || echo "LS_COLORS is not set"
LS_COLORS is set
The output of dircolors -b
is a set of shell commands to set the variable!
That’s why .bashrc uses eval to execute it.
$ dircolors -b
LS_COLORS='...';
export LS_COLORS
The .bashrc first attempts to read color configuration from ~/.dircolors
before using the default colors. So I think to change the colors I can write
such a file.
But where are the defaults?
From man dir_colors
:
The program ls(1) uses the environment variable LS_COLORS to determine the colors in which the filenames are to be displayed. This environment variable is usually set by a command like
eval `dircolors some_path/dir_colors
found in a system default shell initialization file, like /etc/profile or /etc/csh.cshrc. (See also dircolors(1).) Usually, the file used here is /etc/DIR_COLORS and can be overridden by a .dir_colors file in one’s home directory.
The file /etc/DIR_COLORS
does not exist on my system.
From man dircolors
:
If FILE is specified, read it to determine which colors to use for which file types and extensions. Otherwise, a precompiled database is used. For details on the format of these files, run
dircolors --print-database
.
The database contains config for other-writable directories. We saw that the backup directory is other-writable.
STICKY_OTHER_WRITABLE 30;42 # dir that is sticky and other-writable (+t,o+w)
OTHER_WRITABLE 34;42 # dir that is other-writable (o+w) and not sticky
STICKY 37;44 # dir with the sticky bit set (+t) and not other-writable
But these values don’t appear in the LS_COLORS environment variable. The variable contains config only for file extensions.
$ echo $LS_COLORS
rs=0:di=01;34:ln=01;36:mh=00[...]
It turns out that the ls binary has hard-coded defaults. These are the defaults for version 8.30.
static struct bin_str color_indicator[] =
{
{ LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
{ LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
{ 0, NULL }, /* ec: End color (replaces lc+rs+rc) */
{ LEN_STR_PAIR ("0") }, /* rs: Reset to ordinary colors */
{ 0, NULL }, /* no: Normal */
{ 0, NULL }, /* fi: File: default */
{ LEN_STR_PAIR ("01;34") }, /* di: Directory: bright blue */
{ LEN_STR_PAIR ("01;36") }, /* ln: Symlink: bright cyan */
{ LEN_STR_PAIR ("33") }, /* pi: Pipe: yellow/brown */
{ LEN_STR_PAIR ("01;35") }, /* so: Socket: bright magenta */
{ LEN_STR_PAIR ("01;33") }, /* bd: Block device: bright yellow */
{ LEN_STR_PAIR ("01;33") }, /* cd: Char device: bright yellow */
{ 0, NULL }, /* mi: Missing file: undefined */
{ 0, NULL }, /* or: Orphaned symlink: undefined */
{ LEN_STR_PAIR ("01;32") }, /* ex: Executable: bright green */
{ LEN_STR_PAIR ("01;35") }, /* do: Door: bright magenta */
{ LEN_STR_PAIR ("37;41") }, /* su: setuid: white on red */
{ LEN_STR_PAIR ("30;43") }, /* sg: setgid: black on yellow */
{ LEN_STR_PAIR ("37;44") }, /* st: sticky: black on blue */
{ LEN_STR_PAIR ("34;42") }, /* ow: other-writable: blue on green */
{ LEN_STR_PAIR ("30;42") }, /* tw: ow w/ sticky: black on green */
{ LEN_STR_PAIR ("30;41") }, /* ca: black on red */
{ 0, NULL }, /* mh: disabled by default */
{ LEN_STR_PAIR ("\033[K") }, /* cl: clear to end of line */
};
The defaults in the ls binary match those in dircolors’ database.
So now how do you write a custom color config?
This Stack Overflow answer provides a solution. I can write the default database to the file read by my .bashrc.
dircolors --print-database > ~/.dircolors
Now in a new terminal session, I run ls again. So far it still works and the results are unchanged.
total 16896 drwxrwxrwx 1 isme isme 131072 Apr 23 21:06 backup -rwxrwxrwx 1 isme isme 10091479 Apr 29 2020 SamsungPortableSSD_Setup_Mac.pkg -rwxrwxrwx 1 isme isme 6817336 Apr 29 2020 SamsungPortableSSD_Setup_Win.exe -rwxrwxrwx 1 isme isme 118 Jan 20 2016 Samsung portable SSD SW for Android.txt
Now I change the color for OTHER_WRITABLE files to transparent text on a blue
background. Read man dir_colors
to learn the color codes. I can’t find code
07;34
in the documentation, but it renders here as the inverse of the normal
folder (blue text on transparent background).
OTHER_WRITABLE 07;34 # dir that is other-writable (o+w) and not sticky
Using vim to edit the .dircolors file is helpful because it parses the color codes and renders the code in the resulting color.
Testing ls with ls --color=always ~/media/isme/Samsung_T5 | aha
:
total 16896 drwxrwxrwx 1 isme isme 131072 Apr 23 21:06 backup -rwxrwxrwx 1 isme isme 10091479 Apr 29 2020 SamsungPortableSSD_Setup_Mac.pkg -rwxrwxrwx 1 isme isme 6817336 Apr 29 2020 SamsungPortableSSD_Setup_Win.exe -rwxrwxrwx 1 isme isme 118 Jan 20 2016 Samsung portable SSD SW for Android.txt
Testing tree with tree -C /media/isme/Samsung_T5/backup/ | aha
:
/media/isme/Samsung_T5/backup/ ├── config ├── data │ └── 0 │ ├── 0 │ └── 1 ├── hints.1 ├── index.1 ├── integrity.1 ├── nonce └── README 2 directories, 8 files
Done! Now I no longer have to be distracted by unreadable colors in the terminal.