Git Product home page Git Product logo

procs's Introduction

Procs

Go Report Card GoDoc

Procs is a library to make working with command line applications a little nicer.

The primary use case is when you have to use a command line client in place of an API. Often times you want to do things like output stdout within your own logs or ensure that every time the command is called, there are a standard set of flags that are used.

Basic Usage

The majority of this functionality is intended to be included the procs.Process.

Defining a Command

A command can be defined by a string rather than a []string. Normally, this also implies that the library will run the command in a shell, exposing a potential man in the middle attack. Rather than using a shell, procs lexically parses the command for the different arguments. It also allows for pipes in order to string commands together.

p := procs.NewProcess("kubectl get events | grep dev")

You can also define a new Process by passing in predefined commands.

cmds := []*exec.Cmd{
	exec.Command("kubectl", "get", "events"),
	exec.Command("grep", "dev"),
}

p := procs.Process{Cmds: cmds}

Output Handling

One use case that is cumbersome is using the piped output from a command. For example, lets say we wanted to start a couple commands and have each command have its own prefix in stdout, while still capturing the output of the command as-is.

p := procs.NewProcess("cmd1")
p.OutputHandler = func(line string) string {
	fmt.Printf("cmd1 | %s\n")
	return line
}
out, _ := p.Run()
fmt.Println(out)

Whatever is returned from the OutputHandler will be in the buffered output. In this way you can choose to filter or skip output buffering completely.

You can also define a ErrHandler using the same signature to get the same filtering for stderr.

Environment Variables

Rather than use the exec.Cmd []string environment variables, a procs.Process uses a map[string]string for environment variables.

p := procs.NewProcess("echo $FOO")
p.Env = map[string]string{"FOO": "foo"}

Also, environment variables defined by the Process.Env can be expanded automatically using the os.Expand semantics and the provided environment.

There is a ParseEnv function that can help to merge the parent processes' environment with any new values.

env := ParseEnv(os.Environ())
env["USER"] = "foo"

Finally, if you are building commands manually, the Env function can take a map[string]string and convert it to a []string for use with an exec.Cmd. The Env function also accepts a useEnv bool to help include the parent process environment.

cmd := exec.Command("knife", "cookbook", "show", cb)
cmd.Env = Env(map[string]string{"USER": "knife-user"}, true)

Example Applications

Take a look in the cmd dir for some simple applications that use the library. You can also make all to build them. The examples below assume you've built them locally.

Prelog

The prelog command allows running a command and prefixing the output with a value.

$ ./prelog -prefix foo -- echo 'hello world!'
Running the command
foo | hello world!
Accessing the output without a prefix.
hello world!
Running the command with Start / Wait
foo | hello world!

Cmdtmpl

The cmdtmpl command uses the procs.Builder to create a command based on some paramters. It will take a data.yml file and template.yml file to create a command.

$ cat example/data.json
{
  "source": "https://my.example.org",
  "user": "foo",
  "model": "widget",
  "action": "create",
  "args": "-f new -i improved"
}
$ cat example/template.json
[
  "mysvc ${model} ${action} ${args}",
  "--endpoint ${source}",
  "--username ${user}"
]
$ ./cmdtmpl -data example/data.json -template example/template.json
Command: mysvc foo widget create -f new -i imporoved --endpoint https://my.example.org --username foo
$ ./cmdtmpl -data example/data.json -template example/template.json -field user=bar
Command: mysvc foo widget create -f new -i imporoved --endpoint https://my.example.org --username bar

Procmon

The procmon command acts like foreman with the difference being it uses a JSON file with key value pairs instead of a Procfile. This example uses the procs.Manager to manage a set of procs.Processes.

$ cat example/procfile.json
{
  "web": "python -m SimpleHTTPServer"
}
$ ./procmon -procfile example/procfile.json
web | Starting web with python -m SimpleHTTPServer

You can then access http://localhost:8000 to see the logs. You can also kill the child process and see procmon recognizing it has exited and exit itself.

procs's People

Contributors

ionrock avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

procs's Issues

Unicode path is not supported

exePath, _ := filepath.Abs(filepath.Join(wd, "file.exe"))
p := procs.NewProcess(exePath+ " faststart")
err := p.Run()

path:

D:/测试/main.exe

program crashed with output:

Uknown rune: 27979

Security: environment variables are expanded into commands

As mentioned in the reddit post associated with this projects announcement there are some security concerns with this library I believe should at least be clearly documented. While I accept API design is subjective, I believe expanding environment variables and then parsing them as POSIX command lines as demonstrated below is particularly dangerous:

os.Setenv(`VAR`, `FOO | do-rm-rf-cmd`)
p := procs.NewProcess("echo ${VAR}")
fmt.Println(p.Run())
// exec: "do-rm-rf-cmd": executable file not found in $PATH

Even if this project takes the stance that all user input must be trusted and providing safety mechanisms for mistakes is not a worthwhile endeavor- it has the potential for some surprising side affects in regular everyday usage. For example it's not uncommon for "secrets" to contain POSIX shell command characters os.Setenv("SECRET_API_KEY", "K|0HDctF*!$(JF") so it may be worth reconsidering since I can't really think of a use case for this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.