Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndrewRussellHayes/f40fe593623cb9f5c55e9bd4d729f861 to your computer and use it in GitHub Desktop.
Save AndrewRussellHayes/f40fe593623cb9f5c55e9bd4d729f861 to your computer and use it in GitHub Desktop.
Color Terminal for bash/zsh etc..
man() {
env \
LESS_TERMCAP_mb=$(printf "\e[1;31m") \
LESS_TERMCAP_md=$(printf "\e[1;31m") \
LESS_TERMCAP_me=$(printf "\e[0m") \
LESS_TERMCAP_se=$(printf "\e[0m") \
LESS_TERMCAP_so=$(printf "\e[1;44;33m") \
LESS_TERMCAP_ue=$(printf "\e[0m") \
LESS_TERMCAP_us=$(printf "\e[1;32m") \
man "$@"
}
<html>
<body>
<div id="page">
<div id="header">
<div id="headerimg">
<h1><a href="http://boredzo.org/blog/">Idle Time</a></h1>
<div class="description">The personal weblog of Peter Hosey.</div>
</div>
</div>
<div id="content" class="widecolumn">
<div class="navigation">
<div class="alignleft">&laquo; <a href="http://boredzo.org/blog/archives/2016-08-06/back-again" rel="prev">Back again</a></div>
<div class="alignright"></div>
</div>
<div style="clear: both;"></div>
<div class="post" id="post-1655">
<h2 class="posttitle"><a href="http://boredzo.org/blog/archives/2016-08-15/colorized-man-pages-understood-and-customized" rel="bookmark" title="Permanent Link: Colorized man pages: Understood and customized">Colorized man pages: Understood and customized</a></h2>
<p class="timestamp">2016-08-15 21:18:01 UTC <!-- by Peter Hosey --></p>
<div class="entry">
<blockquote class="twitter-tweet" data-cards="hidden" data-lang="en"><p lang="en" dir="ltr">Want colored man pages? Put this in your ~/.bashrc or ~/.zshrc etc directory:<br /><a href="https://gist.github.com/cocoalabs/2fb7dc2199b0d4bf160364b8e557eb66">https://gist.github.com/cocoalabs/2fb7dc2199b0d4bf160364b8e557eb66</a></p>
<p>&mdash; Lemont (@cocoalabs) <a href="https://twitter.com/cocoalabs/status/765305153826107392">August 15, 2016</a></p></blockquote>
<pre><code>man() {
env \
LESS_TERMCAP_mb=$(printf "\e[1;31m") \
LESS_TERMCAP_md=$(printf "\e[1;31m") \
LESS_TERMCAP_me=$(printf "\e[0m") \
LESS_TERMCAP_se=$(printf "\e[0m") \
LESS_TERMCAP_so=$(printf "\e[1;44;33m") \
LESS_TERMCAP_ue=$(printf "\e[0m") \
LESS_TERMCAP_us=$(printf "\e[1;32m") \
man "$@"
}
</code></pre>
<p class="screenshot"><img width="500" height="493" alt="Screenshot of Terminal showing the zsh manpage with the above customizations." src="http://boredzo.org/blog/wp-content/uploads/2016/08/terminal-colorman-initial-basictheme.png" /></p>
<p>Pretty neat, right? Let&#8217;s tear it apart, see what it&#8217;s doing, and think about how we might customize it to suit ourselves.</p>
<p><span id="more-1655"></span><br />
<h2>What even</h2>
<p>The first and last lines define a shell function. Your shell recognizes these as commands, just like the tools that are installed on the system, but they&#8217;re written in shell language—like shell scripts, but recorded in memory rather than saved as files.</p>
<p>This shell function is named “<code>man</code>”—yes, the same name as the tool that shows manpages! Your shell will prefer your own functions over the pre-installed tools.</p>
<p>This function spans many lines, but runs only one command—the backslashes (<code>\</code>) escape the line breaks so that the shell interprets everything from “<code>env</code>” unto the first line not ending with a <code>\</code> as one line.</p>
<p>That last physical line is “<code>man "%@"</code>”. But wait, you might ask—how doesn&#8217;t this just call itself?</p>
<p>This whole logical line runs a command called <code>env</code>. It passes <code>env</code> a bunch of environment variables (e.g., <code>LESS_TERMCAP_us=…</code>) to set, and then a command to run with that environment. That command, here, is <code>man "%@"</code>.</p>
<p><code>env</code> doesn&#8217;t know about shell functions. It&#8217;s not part of the shell; it&#8217;s a separate tool. So it doesn&#8217;t know about our function—the only <code>man</code> it knows about is the pre-installed tool called <code>man</code>.</p>
<p>So the one line that is the body of this function uses the tool called <code>env</code> to set up a custom environment in which to then run the tool called <code>man</code>.</p>
<p><code>"%@"</code> tells the shell to insert all of the arguments to the function there, so that that line will run the real <code>man</code> with the arguments you passed in to the fake <code>man</code>—most probably, the optional section number and the manpage name.</p>
<h3>Cool, but what&#8217;s <code>LESS_TERMCAP_mb</code> and such?</h3>
<p><a href="x-man-page://1/less"><code>less</code></a> is a pager—a tool that shows you a screenful at a time of some output, and waits for you to acknowledge it before showing you more. (It&#8217;s an expansion of an older pager called <code>more</code>.) It&#8217;s what <code>man</code> uses to do that—<code>man</code> just runs <code>less</code> and pipes the rendered manpage text into it.</p>
<p>I don&#8217;t know that the <code>LESS_TERMCAP_xx</code> trick is actually documented—it&#8217;s not mentioned in less&#8217;s manpage. But here&#8217;s what it does:</p>
<p><a href="http://linux.die.net/man/5/termcap">termcap</a> is a database of terminal capabilities. (I won&#8217;t fault you for imagining a terminal in a cape right now.) The idea is that every kind of terminal is different (which it was, back in the day), so there needs to be a database in which you can look up whichever terminal the user has in order to find out what that kind of terminal is capable of and how to tell it to do each thing.</p>
<p>Each capability has a two-letter identifier, and maps to a string of characters. Here are the capabilities used in the Gist, as defined in <a href="http://linux.die.net/man/5/termcap">the aforelinked manpage</a>:</p>
<dl>
<dt>mb</dt>
<dd>Start blinking</dd>
<dt>md</dt>
<dd>Start bold mode</dd>
<dt>me</dt>
<dd>End all mode like so, us, mb, md and mr</dd>
<dt>so</dt>
<dd>Start standout mode</dd>
<dt>se</dt>
<dd>End standout mode</dd>
<dt>us</dt>
<dd>Start underlining</dd>
<dt>ue</dt>
<dd>End underlining</dd>
</dl>
<p>“Standout” mode, in case you&#8217;re wondering, is inverse video: use the assigned foreground color as the background color and <span lang="la">vice versa</span>.</p>
<p>zsh has a built-in command called <code>echotc</code> that lets us play with these records. For example:</p>
<pre>% <kbd>echotc us && echo -n 'This should be underlined' && \
echotc ue && echo ' and this should not.'</kbd>
<span style="text-decoration: underline">This should be underlined</span> and this should not.</pre>
<p>Yup, <code>us</code> starts underlining and <code>ue</code> ends underlining!</p>
<p>So when we want something underlined (for example), the <code>us</code> and <code>ue</code> entries in our terminal&#8217;s termcap record are what we need to send to the terminal to start and end underlining that section of text.</p>
<p>And <code>less</code>, it seems, provides this handy way to <em>override</em> those entries using environment variables. We can make <code>us</code> and <code>ue</code> and any other termcap string do whatever we want!</p>
<h3>What are these strings you speak of?</h3>
<p>Yes, just what are these termcap strings doing now?</p>
<p>All of them follow a similar format:</p>
<pre>\e[(one or more numbers separated by semicolons)m</pre>
<p>The <code>\e[</code> essentially tells the terminal to start listening for a command that will change its behavior. <code>m</code> is actually the command here; all the inputs to the command come before it. The <code>m</code> command tells the terminal to change how it renders subsequent text until further notice.</p>
<p>(<code>\e</code> is the ASCII/Unicode character U+001B ESCAPE, just in case you were wondering. In other languages, you might refer to it as <code>\x1b</code> or <code>\033</code>.)</p>
<p>These are <a href="https://en.wikipedia.org/wiki/ANSI_escape_code">ANSI escape sequences</a>, which come in a lot more varieties than these, but these are the most common. The <code>m</code> command takes a list of arguments, all of which are numbers, separated by semicolons:</p>
<dl>
<dt>0</dt>
<dd>Reset to standard configuration</dd>
<dt>1</dt>
<dd>Bold</dd>
<dt>31</dt>
<dd>Set foreground color to red</dd>
<dt>32</dt>
<dd>Set foreground color to green</dd>
<dt>33</dt>
<dd>Set foreground color to yellow</dd>
<dt>44</dt>
<dd>Set background color to blue</dd>
</dl>
<p>There are more than just those options for our foreground and background colors, of course! We&#8217;ll see more shortly.</p>
<p>So, for example, the first variable:</p>
<pre><code>LESS_TERMCAP_mb=$(printf "\e[1;31m") \
</code></pre>
<p>says that when we want to start blinking, display bold red text instead.</p>
<h2>Customizing</h2>
<p>Let&#8217;s start by seeing it in my own Terminal theme:</p>
<p class="screenshot"><img width="483" height="600" alt="Screenshot of Terminal showing the zsh manpage with the above customizations." src="http://boredzo.org/blog/wp-content/uploads/2016/08/terminal-colorman-initial.png" /></p>
<p>I&#8217;m also going to make one change to start with:</p>
<pre><code>man() {
env \
LESS_TERMCAP_mb=$'\e[1;31m' \
LESS_TERMCAP_md=$'\e[1;31m' \
LESS_TERMCAP_me=$'\e[0m' \
LESS_TERMCAP_se=$'\e[0m' \
LESS_TERMCAP_so=$'\e[1;44;33m' \
LESS_TERMCAP_ue=$'\e[0m' \
LESS_TERMCAP_us=$'\e[1;32m' \
man "$@"
}
</code></pre>
<p><code>$(…)</code> runs a command and is replaced with the output, so <code>$(printf …)</code> replaces the <code>$(…)</code> with the string that <code>printf</code> prints.</p>
<p>That&#8217;s redundant! We can simplify this to <code>$'…'</code>, which will let us have the <code>\e</code> sequences without calling the shell built-in function <code>printf</code> for every one of these.</p>
<p>OK, let&#8217;s start with…</p>
<h3>The bountiful rainbow of color</h3>
<p>We have a lot more than those four colors to choose from. The basic ANSI set is 16, and most terminal emulators have supported 256-color sequences for years now. <a href="https://en.wikipedia.org/wiki/ANSI_escape_code">That Wikipedia article</a> has charts of all the different color codes; here are the basic groups:</p>
<dl>
<dt>30–37</dt>
<dd>Foreground color, dark</dd>
<dt>90–97</dt>
<dd>Foreground color, bright</dd>
<dt>40–47</dt>
<dd>Background color, dark</dd>
<dt>100–107</dt>
<dd>Background color, bright</dd>
<dt>38;5;<var>###</var></dt>
<dd>Set foreground color to color <var>###</var> (256-color extension)</dd>
<dt>48;5;<var>###</var></dt>
<dd>Set background color to color <var>###</var> (256-color extension)</dd>
</dl>
<p>Now that we have more colors to work with, let&#8217;s explore…</p>
<h3>What things in a manpage use which capabilities?</h3>
<p>I changed the function so that every capability got a unique character—i.e., I changed blinking (<code>mb</code>) to 35, which is fuchsia—and then looked up a few things.</p>
<p>It doesn&#8217;t seem like anything uses blinking.</p>
<p>That leaves bold (<code>mb</code>), underline (<code>us</code>), and standout/inverse (<code>so</code>).</p>
<p><code>man</code> and <code>less</code> use:</p>
<ul>
<li>bold for headings, command synopses, and code font</li>
<li>underline for proper names (for example, “termcap” and “terminfo” in the termcap manpage), variable names (“name”, “bp”, “id”, etc.), and type names in some manpages (such as <a href="x-man-page://3/dispatch_queue_create"><code>dispatch_queue_create(3)</code></a>)</li>
<li>inverse for the prompt at the bottom</li>
</ul>
<p>(Some of these are more consistent than others. This is the problem with looking things from the styling end rather than the semantic end—which is why web developers had to invent CSS!)</p>
<h3>My own changes</h3>
<p><a href="https://gist.github.com/boredzo/06271944983864da495d303638351ca8">I made these changes</a>:</p>
<ul>
<li>Cut out <code>mb</code> entirely, since it seems unused</li>
<li>Change <code>md</code> from setting <code>31</code> (red) to setting <code>36</code> (cyan) for the foreground color</li>
<li>Change <code>so</code> from <code>44;33</code> (yellow on blue) to <code>40;92</code> (bright green on black)</li>
</ul>
<p>I left the <code>us</code> override alone; I think the green generally works where that&#8217;s used.</p>
<p>So here&#8217;s what that looks like:</p>
<p class="screenshot"><img width="483" height="600" alt="Screenshot of the zsh manpage with the customized style." src="http://boredzo.org/blog/wp-content/uploads/2016/08/customized-zsh.png" /><br />
<img width="483" height="600" alt="Screenshot of the dispatch_queue_create manpage with the customized style." src="http://boredzo.org/blog/wp-content/uploads/2016/08/customized-dispatch_queue_create.png" /></p>
<p class="postmetadata">Categories: <a href="http://boredzo.org/blog/archives/category/programming" rel="category tag">Programming</a>; <a href="http://boredzo.org/blog/archives/category/programming/toolchain" rel="category tag">Toolchain</a>; <a href="http://boredzo.org/blog/archives/category/programming/undocumentedgoodness" rel="category tag">UndocumentedGoodness</a>. | <a href="#comments">Comments: 4</a> (<a href="http://boredzo.org/blog/archives/2016-08-15/colorized-man-pages-understood-and-customized/feed">feed</a>).</p>
</div>
</div>
<!-- You can start editing here. -->
<h3 id="comments">4 Responses to &#8220;Colorized man pages: Understood and customized&#8221;</h3>
<ol class="commentlist">
<li class="comment alt " id="comment-822111">
<cite><a href='http://webonastick.com/' rel='external nofollow' class='url'>Darren Embry</a></cite> Says:
<br />
<small class="commentmetadata"><a href="#comment-822111" title="">August 16th, 2016 at 19:59:41</a> </small>
<p>A modified version of this that does not invoke external programs other than man. Works in bash and zsh.</p>
<p> man () {<br />
LESS_TERMCAP_mb=$&#8217;\e'&#8221;[1;31m&#8221; \<br />
LESS_TERMCAP_md=$&#8217;\e'&#8221;[1;31m&#8221; \<br />
LESS_TERMCAP_me=$&#8217;\e'&#8221;[0m&#8221; \<br />
LESS_TERMCAP_se=$&#8217;\e'&#8221;[0m&#8221; \<br />
LESS_TERMCAP_so=$&#8217;\e'&#8221;[1;44;33m&#8221; \<br />
LESS_TERMCAP_ue=$&#8217;\e'&#8221;[0m&#8221; \<br />
LESS_TERMCAP_us=$&#8217;\e'&#8221;[1;32m&#8221; \<br />
command man &#8220;$@&#8221;<br />
}</p>
<p>The $&#8217;&#8230;&#8217; construct is for escape sequences, e.g., $&#8217;\e&#8217;. The `command` builtin in bash and zsh avoids recursively invoking the alias. You can specify = as many times as you want before specifying a command and arguments to make those variable assignments temporary to the invocation of that command. No `env` required.</p>
</li>
<li class="comment " id="comment-822114">
<cite><a href='http://webonastick.com/' rel='external nofollow' class='url'>Darren Embry</a></cite> Says:
<br />
<small class="commentmetadata"><a href="#comment-822114" title="">August 16th, 2016 at 20:01:44</a> </small>
<p>That should say: you can specify <i>var</i>=<i>val</i> as many times&#8230;</p>
</li>
<li class="comment alt " id="comment-822115">
<cite>Adam</cite> Says:
<br />
<small class="commentmetadata"><a href="#comment-822115" title="">August 16th, 2016 at 21:42:17</a> </small>
<p>On my Mac (10.10) I had to put this in ~/.bash_profile instead of ~/.bash_rc for the man override to stick, in case anyone else has this problem.</p>
</li>
<li class="comment " id="comment-822120">
<cite>John Macdonald</cite> Says:
<br />
<small class="commentmetadata"><a href="#comment-822120" title="">August 17th, 2016 at 07:35:17</a> </small>
<p>Adam, it is usually ~/.bashrc (with no underscore). ~/.bash_profile *does* have the underscore, so that works too &#8211; it just sets up the same command for your non-interactive shell sessions as well as the interactive ones, which wastes a few microseconds of startup time and a bit (well, bytes) of memory.</p>
</li>
</ol>
<h3 id="respond">Leave a Reply</h3>
<form action="http://boredzo.org/blog/wp-comments-post.php" method="post" id="commentform">
<p><input type="text" name="author" id="author" value="" size="22" tabindex="1" />
<label for="author"><small>Name (required)</small></label></p>
<p><input type="text" name="email" id="email" value="" size="22" tabindex="2" />
<label for="email"><small>Mail (will not be published) (required)</small></label></p>
<p><input type="text" name="url" id="url" value="" size="22" tabindex="3" />
<label for="url"><small>Website</small></label></p>
<fieldset id="negative_turing_test">
<p><label for="ntt_response">Spam-prevention question: </label>
<input type="text" name="ntt_response" size="50" value="The eighth word in this sentence is wrong. Change it to be right." tabindex="4" /></p>
<p>Do not delete the second sentence.</p> </fieldset>
<!--<p><small><strong>XHTML:</strong> You can use these tags: &lt;a href=&quot;&quot; title=&quot;&quot;&gt; &lt;abbr title=&quot;&quot;&gt; &lt;acronym title=&quot;&quot;&gt; &lt;b&gt; &lt;blockquote cite=&quot;&quot;&gt; &lt;cite&gt; &lt;code&gt; &lt;del datetime=&quot;&quot;&gt; &lt;em&gt; &lt;i&gt; &lt;q cite=&quot;&quot;&gt; &lt;s&gt; &lt;strike&gt; &lt;strong&gt; </small></p>-->
<p><textarea name="comment" id="comment" cols="100%" rows="10" tabindex="4"></textarea></p>
<p><input name="submit" type="submit" id="submit" tabindex="5" value="Submit Comment" />
<input type="hidden" name="comment_post_ID" value="1655" />
</p>
</form>
</div>
<div id="footer">
<!-- If you'd like to support WordPress, having the "powered by" link someone on your blog is the best way, it's our only promotion or advertising. -->
<p>
Idle Time runs on
<a href="http://wordpress.org/">WordPress</a>.
</p>
<p>Feeds are available for <a href="feed://boredzo.org/blog/feed">entries</a>
and <a href="feed://boredzo.org/blog/comments/feed">comments</a>.
<!-- 25 queries. 0.208 seconds. -->
</p>
</div>
</div>
<!-- Gorgeous design by Michael Heilemann - http://binarybonsai.com/kubrick/ -->
<script type='text/javascript' src='http://boredzo.org/blog/wp-includes/js/wp-embed.min.js?ver=4.5.3'></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment