goman - the missing man pages for Go binaries
Most Go binaries come without any man page. The tool goman
fills this gap. If the corresponding project includes a decent README file (and most projects do), goman
find this README file and displays it on the terminal.
It kept happening to me: I type man <blah>
to get the man page of <blah>
, only to find out that <blah>
is a Go binary and hence has no man page (except for rare cases where the author took the time to write one and distribute it along with the binary via some installation manager like
Homebrew.)
Well, not anymore.
I wrote a tool named
goman
to get me some info about a Go binary when man
can't.
From now on, when man
finds no man page, goman
will take a second attempt and try displaying the README file from the binary's project sources. This usually succeeds if the binary is a Go binary, and if this binary has a corresponding project either locally or on GitHub or GitLab.
This demo shows
goman
,goman
withless -R
, andgoman
launched by a bash script ifman
finds no man page.
[ ]media/goman.gif (Click to enlarge)
Here is how it works.
Step 1: Locate the binary
A Go binary must reside within one of the directories contained in $PATH
, or else I would not be able to call it from the command line. And when I run man <binary>
, then the binary must be the first one of that name. So to find the binary, we simply need to do the same as the command which
does.
Luckily, this is as easy as go-getting the which
package from
bfontaine/which and calling which.One(name)
. Additionally, goman
also calls which.OneWithPath(name, path)
in order to locate the binary also in $GOPATH/bin
or in the current path, in case any of the two is not included in $PATH
.
Step 2: Get the binary to reveal its source
Here is a maybe little-known fact: Go binaries contain the path of their source code project. With the help of the packages debug/elf
, debug/gosym
, and debug/macho
, goman
dives into the symbol table of the binary and locates the symbol “main.main”. The text of this symbol is the path to the source code, relative to GOPATH (and sometimes it is an absolute path, but in this case, goman
takes a guess and cuts off the path prefix up to the first occurrence of /src/
, which is supposed to be the /src/
directory right beneath GOPATH).
The code for reading the symbol table is gratefully taken from the
gorebuild
tool. As gorebuild
is no library, I copied over dwarf.go
that contains all the code I need for that behind a simple function call: getMainPath(file)
.
Step 3: Locate the README file
This part turned out to be a bit more complex. goman
needs to find a README file that is either a Markdown file or a plain text file, which means we need to look for several possible file extensions: .md
, .markdown
, .txt
, and no extension at all. (GitHub supports other markup langauges besides Markdown, but I find they are so rare that they can be safely ignored.)
In addition to that, a command can either reside in the project root, or in a /cmd/abc
subproject. Sometimes the /cmd/abc
subprojects contain their own README file but in most cases they don't. So goman
has to check both the subbroject and the root project for a README file.
And if this is not enough, the source code may or may not exist on the local machine. The Go binary might have been installed via Homebrew or some other package manager, or the source code might have been removed after installing the binary.
In this case, goman
must also look into the public repository of the project (which nowadays is mostly hosted on GitHub, with a few exceptions).
To make things even more complicated, the raw text version of the README file resides at a different path than the local README, so goman
takes an additional step and builds the raw file URL from what it knows about the source path and about GitHub's and GitLab's raw file URL structure. (I had no joy with Bitbucket's file URL's, as they contain unpredictable hash values. I did not want to go as far as writing a web scraper to analyze the repository page in order to get the README URL.)
This diagram shows where goman
searches for READMEs - if you seek some diversion for a few seconds, click the play button.
Step 4: Render Markdown as colored ANSI text
Markdown renderers usually produce HTML, in some cases PDF, and, if they value traditional typesetting, also LaTeX. Plain text with ANSI color codes is far less widespread.
Again, luck was on my side. This fork of blackfriday implements an ANSI renderer for Markdown. So getting color-coded ANSI output from a README file required little more than copying a couple of lines from mdcat that uses the ANSI renderer under the hood.
Done.
Get goman
Update: goman
is available via Homebrew on macOS, Linux, and WSL:
brew tap appliedgo/tools
brew install goman
(Read more about providing your Go tools via Homebrew here.)
Get goman
via go get
:
go get -u github.com/appliedgocode/goman
Usage
It's all in the
README, so simply type goman goman
;-)
TL;DR:
goman <binary> # find and display the README of <binary>
goman <binary> | less -R # same but with paging
And a shell script can make goman
blend in with the standard man
command, to auto-reveal the README if the binary has no man page. (See the README for instructions.)
Happy coding!
Update 2020-12-14: moved repo from christophberger/goman to appliedgocode/goman
Update 2021-05-10: goman
can be installed via Homebrew