All Projects → go-cmd → Cmd

go-cmd / Cmd

Licence: mit
Non-blocking external commands in Go with and streaming output and concurrent-safe access

Programming Languages

go
31211 projects - #10 most used programming language
golang
3204 projects

Projects that are alternatives of or similar to Cmd

Parallel Ssh
Asynchronous parallel SSH client library.
Stars: ✭ 864 (+81.13%)
Mutual labels:  asynchronous, non-blocking
Ws Machine
WS-Machine is a websocket finite state machine for client websocket connections (Go)
Stars: ✭ 110 (-76.94%)
Mutual labels:  asynchronous, non-blocking
Zsh Autocomplete
🤖 Real-time type-ahead completion for Zsh. Asynchronous find-as-you-type autocompletion.
Stars: ✭ 641 (+34.38%)
Mutual labels:  asynchronous, non-blocking
Mio
Metal IO library for Rust
Stars: ✭ 4,613 (+867.09%)
Mutual labels:  asynchronous, non-blocking
Drone Core
The core crate for Drone, an Embedded Operating System.
Stars: ✭ 263 (-44.86%)
Mutual labels:  asynchronous, real-time
Drone
CLI utility for Drone, an Embedded Operating System.
Stars: ✭ 114 (-76.1%)
Mutual labels:  asynchronous, real-time
Base64 Async
Non-blocking chunked Base64 encoding
Stars: ✭ 98 (-79.45%)
Mutual labels:  asynchronous, non-blocking
Lightning
A Swift Multiplatform Single-threaded Non-blocking Web and Networking Framework
Stars: ✭ 312 (-34.59%)
Mutual labels:  asynchronous, non-blocking
RxIo
Asynchronous non-blocking File Reader and Writer library for Java
Stars: ✭ 20 (-95.81%)
Mutual labels:  asynchronous, non-blocking
Future
High-performance Future implementation for the JVM
Stars: ✭ 223 (-53.25%)
Mutual labels:  asynchronous, non-blocking
Clearly
Clearly see and debug your celery cluster in real time!
Stars: ✭ 287 (-39.83%)
Mutual labels:  asynchronous, real-time
Message Io
Event-driven message library for building network applications easy and fast.
Stars: ✭ 321 (-32.7%)
Mutual labels:  asynchronous, non-blocking
Tornado
Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed.
Stars: ✭ 20,317 (+4159.33%)
Mutual labels:  asynchronous
Multinet
Real-time Joint Semantic Reasoning for Autonomous Driving
Stars: ✭ 471 (-1.26%)
Mutual labels:  real-time
Primus
⚡ Primus, the creator god of the transformers & an abstraction layer for real-time to prevent module lock-in.
Stars: ✭ 4,302 (+801.89%)
Mutual labels:  real-time
Ssr Net
[IJCAI18] SSR-Net: A Compact Soft Stagewise Regression Network for Age Estimation
Stars: ✭ 475 (-0.42%)
Mutual labels:  real-time
Zephyr
Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
Stars: ✭ 5,335 (+1018.45%)
Mutual labels:  real-time
Engine.io
socket.io
Stars: ✭ 4,329 (+807.55%)
Mutual labels:  real-time
Centrifuge
Real-time messaging library for Go with scalability in mind
Stars: ✭ 446 (-6.5%)
Mutual labels:  real-time
Openpose unity plugin
OpenPose's Unity Plugin for Unity users
Stars: ✭ 446 (-6.5%)
Mutual labels:  real-time

go-cmd/Cmd

Go Report Card Build status Coverage report GoDoc

This package is a small but very useful wrapper around os/exec.Cmd that makes it safe and simple to run external commands in highly concurrent, asynchronous, real-time applications. It works on Linux, macOS, and Windows. Here's the basic usage:

import (
	"fmt"
	"time"
	"github.com/go-cmd/cmd"
)

func main() {
	// Start a long-running process, capture stdout and stderr
	findCmd := cmd.NewCmd("find", "/", "--name", "needle")
	statusChan := findCmd.Start() // non-blocking

	ticker := time.NewTicker(2 * time.Second)

	// Print last line of stdout every 2s
	go func() {
		for range ticker.C {
			status := findCmd.Status()
			n := len(status.Stdout)
			fmt.Println(status.Stdout[n-1])
		}
	}()

	// Stop command after 1 hour
	go func() {
		<-time.After(1 * time.Hour)
		findCmd.Stop()
	}()

	// Check if command is done
	select {
	case finalStatus := <-statusChan:
		// done
	default:
		// no, still running
	}

	// Block waiting for command to exit, be stopped, or be killed
	finalStatus := <-statusChan
}

That's it, only three methods: Start, Stop, and Status. When possible, it's better to use go-cmd/Cmd than os/exec.Cmd because go-cmd/Cmd provides:

  1. Channel-based fire and forget
  2. Real-time stdout and stderr
  3. Real-time status
  4. Complete and consolidated return
  5. Proper process termination
  6. 100% test coverage, no race conditions

Channel-based fire and forget

As the example above shows, starting a command immediately returns a channel to which the final status is sent when the command exits for any reason. So by default commands run asynchronously, but running synchronously is possible and easy, too:

// Run foo and block waiting for it to exit
c := cmd.NewCmd("foo")
s := <-c.Start()

To achieve similar with os/exec.Cmd requires everything this package already does.

Real-time stdout and stderr

It's common to want to read stdout or stderr while the command is running. The common approach is to call StdoutPipe and read from the provided io.ReadCloser. This works but it's wrong because it causes a race condition (that go test -race detects) and the docs say it's wrong: "it is incorrect to call Wait before all reads from the pipe have completed. [...] it is incorrect to call Run when using StdoutPipe".

The proper solution is to set the io.Writer of Stdout. To be thread-safe and non-racey, this requires further work to write while possibly N-many goroutines read. go-cmd/Cmd has done this work.

Real-time status

Similar to real-time stdout and stderr, it's nice to see, for example, elapsed runtime. This package allows that: Status can be called any time by any goroutine, and it returns this struct:

type Status struct {
    Cmd      string
    PID      int
    Complete bool
    Exit     int
    Error    error
    Runtime  float64 // seconds
    Stdout   []string
    Stderr   []string
}

Complete and consolidated return

Speaking of that struct above, Go built-in Cmd does not put all the return information in one place, which is fine because Go is awesome! But to save some time, go-cmd/Cmd uses the Status struct above to convey all information about the command. Even when the command finishes, calling Status returns the final status, the same final status sent to the status channel returned by the call to Start.

Proper process termination

os/exec/Cmd.Wait can block even after the command is killed. That can be surprising and cause problems. But go-cmd/Cmd.Stop reliably terminates the command, no surprises. The issue has to do with process group IDs. It's common to kill the command PID, but usually one needs to kill its process group ID instead. go-cmd/Cmd.Stop implements the necessary low-level magic to make this happen.

100% test coverage, no race conditions

In addition to 100% test coverage and no race conditions, this package is actively used in production environments.


Acknowledgements

Brian Ip wrote the original code to get the exit status. Strangely, Go doesn't just provide this, it requires magic like exiterr.Sys().(syscall.WaitStatus) and more.


License

MIT © go-Cmd.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].