-
-
Save dchapes/1d0c538ce07902b76c75 to your computer and use it in GitHub Desktop.
Simple pager Go package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The pager package allows the program to easily pipe it's | |
// standard output through a pager program | |
// (like how the man command does). | |
package pager | |
import ( | |
"errors" | |
"os" | |
"os/exec" | |
"strings" | |
) | |
var pager struct { | |
cmd *exec.Cmd | |
file *os.File | |
} | |
// The environment variables to check for the name of (and arguments to) | |
// the pager to run. | |
var PagerEnvVariables = []string{"PAGER"} | |
// The command names in $PATH to look for if none of the environment | |
// variables are set. | |
// Cannot include arguments. | |
var PagerCommands = []string{"less", "more"} | |
func pagerExecPath() (path string, args []string, err error) { | |
for _, testVar := range PagerEnvVariables { | |
path = os.Getenv(testVar) | |
if path != "" { | |
// BUG: does not handle multiple spaces, e.g.: "less -s -R" | |
args = strings.Split(path, " ") | |
return args[0], args[1:], nil | |
} | |
} | |
// This default only gets used if PagerCommands is empty. | |
err = exec.ErrNotFound | |
for _, testPath := range PagerCommands { | |
path, err = exec.LookPath(testPath) | |
if err == nil { | |
return path, nil, nil | |
} | |
} | |
return "", nil, err | |
} | |
// New returns a new os.File connected to a pager. | |
// The returned file can be used as a replacement to os.Stdout, | |
// everything written to it is piped to a pager. | |
// To determine what pager to run, the environment variables listed | |
// in PagerEnvVariables are checked. | |
// If all are empty/unset then the commands listed in PagerCommands | |
// are looked for in $PATH. | |
func New() (*os.File, error) { | |
if pager.cmd != nil { | |
return nil, errors.New("pager: already exists") | |
} | |
path, args, err := pagerExecPath() | |
if err != nil { | |
return nil, err | |
} | |
pager.cmd = exec.Command(path, args...) | |
pager.cmd.Stdout = os.Stdout | |
pager.cmd.Stderr = os.Stderr | |
w, err := pager.cmd.StdinPipe() | |
if err != nil { | |
return nil, err | |
} | |
f, ok := w.(*os.File) | |
if !ok { | |
return nil, errors.New("pager: exec.Command.StdinPipe did not return type *os.File") | |
} | |
pager.file = f | |
err = pager.cmd.Start() | |
if err != nil { | |
return nil, err | |
} | |
return pager.file, nil | |
} | |
// Stdout sets the global variable os.Stdout to the result of New() | |
// and returns the old os.Stdout value. | |
func Stdout() (oldStdout *os.File, err error) { | |
oldStdout = os.Stdout | |
output, err := New() | |
if err != nil { | |
return | |
} | |
os.Stdout = output | |
return | |
} | |
// Wait closes the pipe to the pager setup with New() or Stdout() and waits | |
// for it to exit. | |
// | |
// This should normally be called before the program exists, | |
// typically via a defer call in main(). | |
func Wait() { | |
if pager.cmd == nil { | |
return | |
} | |
pager.file.Close() | |
pager.cmd.Wait() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
PSA: On line 32 replace
strings.Split()
withstrings.Fields()
to fix the mentioned bug.