I have a go program that should invoke a ruby script.
I have a runCommand function:
func runCommand(cmdName string, arg ...string) {
cmd := exec.Command(cmdName, arg...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
err = cmd.Run()
if err != nil {
fmt.Printf("Failed to start Ruby. %s\n", err.Error())
os.Exit(1)
}
}
I invoke it like this:
runCommand("ruby", "-e", "require 'foo'")
It works for most cases, except if there is a gets or any similar operation in the child process that needs to pause for an input.
I have tried setting cmd.Stdin = os.Stdin, but it does not wait for input.
What am I doing wrong?
The following program seems do what you ask for (my runCommand is almost identical to yours. I just changed the = to := for the err line.) Are you doing something differently?
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
runCommand("ruby", "-e", `puts "Running"; $in = gets; puts "You said #{$in}"`)
}
func runCommand(cmdName string, arg ...string) {
cmd := exec.Command(cmdName, arg...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
err := cmd.Run()
if err != nil {
fmt.Printf("Failed to start Ruby. %s\n", err.Error())
os.Exit(1)
}
}
You might need to use a pseudoterminal. You can do this in go with this library: github.com/kr/pty:
package main
import (
"bufio"
"io"
"log"
"os"
"os/exec"
"github.com/kr/pty"
)
func runCommand(cmdName string, arg ...string) {
cmd := exec.Command(cmdName, arg...)
tty, err := pty.Start(cmd)
if err != nil {
log.Fatalln(err)
}
defer tty.Close()
go func() {
scanner := bufio.NewScanner(tty)
for scanner.Scan() {
log.Println("[" + cmdName + "] " + scanner.Text())
}
}()
go func() {
io.Copy(tty, os.Stdin)
}()
err = cmd.Wait()
if err != nil {
log.Fatalln(err)
}
}
func main() {
log.SetFlags(0)
runCommand("ruby", "-e", `
puts "Enter some text"
text = gets
puts text
`)
}
Related
I am calling a python script from Go code:
package main
import (
"os/exec"
"os"
"fmt"
"time"
"encoding/json"
)
func main() {
cmd := exec.Command("python","/home/devendra/Desktop/sync/blur_multithread.py","http://4.imimg.com/data4/TP/ED/NSDMERP-28759633/audiovideojocks.png")
var logs=make(map[string]interface{})
logs["tes"]=os.Stdout
_ = cmd.Run()
WriteLogs(logs)//Writelog is my function which logs everything in a file
}
func WriteLogs(logs map[string]interface{}){
currentTime := time.Now().Local()
jsonLog, err := json.Marshal(logs)
if err != nil {
fmt.Println(err.Error())
}
jsonLogString := string(jsonLog[:len(jsonLog)])
logfile := "/home/devendra/ImageServiceLogs/"+ "ImageServiceLogs_" + currentTime.Format("2006-01-02") + ".txt"
if logfile == "" {
fmt.Println("Could not find logfile in configuration ...!!!")
} else {
jsonLogFile, err := os.OpenFile(logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
defer jsonLogFile.Close()
if err != nil {
fmt.Println(err.Error())
}
jsonLogFile.WriteString(jsonLogString + "\n")
}
}
But in the logs value of tes field is null while my python script is giving me output. How to get the output of python script in my code?
As per official documentation examples, exec.Cmd.Ouput() ([]byte, error) will give you the sdout of the command after it has finished running.
https://golang.org/pkg/os/exec/#Cmd.Output
package main
import (
"fmt"
"log"
"os/exec"
)
func main() {
out, err := exec.Command("date").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
To receive both stdout and stderr of the process, writer should use exec.Cmd.CombinedOutput
https://golang.org/pkg/os/exec/#Cmd.CombinedOutput
If someone wants to receive the command output in real time to its terminal, then the writer should set exec.Cmd.Stdout and exec.Cmd.Stderr properties to, respectively, os.Stdout and os.Stderr and invoke the exec.Cmd.Run() error method.
https://golang.org/pkg/os/exec/#Cmd
https://golang.org/pkg/os/exec/#Cmd.Run
package main
import (
"fmt"
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("date")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
To forward outputs and capture it at the same time, writer should seek help of the io package to use an io.MultiWriter
https://golang.org/pkg/io/#MultiWriter
package main
import (
"fmt"
"io"
"log"
"os"
"os/exec"
)
func main() {
stdout := new(bytes.Bufer)
stderr := new(bytes.Bufer)
cmd := exec.Command("date")
cmd.Stdout = io.MultiWriter(os.Stdout, stdout)
cmd.Stderr = io.MultiWriter(os.Stderr, stderr)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
Alternatively, you can make use of the exec.Cmd.{Stdout,Stderr}Pipe() see the official documentation examples https://golang.org/pkg/os/exec/#Cmd.StdoutPipe
I've been trying to get the exec stdoutpipe from ffmpeg and write it into a different file. However, it stalls and doesn't finish executing the command.
package main
import (
"bytes"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
)
func stdinfill(stdin io.WriteCloser) {
fi, err := ioutil.ReadFile("music.ogg")
if err != nil {
log.Fatal(err)
}
io.Copy(stdin, bytes.NewReader(fi))
}
func main() {
runcommand()
}
func runcommand() {
cmd := exec.Command("ffmpeg", "-i", "pipe:0", "-f", "mp3", "pipe:1")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
log.Fatal(err)
}
stdinfill(stdin)
fo, err := os.Create("output.mp3")
if err != nil {
log.Fatal(err)
}
io.Copy(fo, stdout)
defer fo.Close()
err = cmd.Wait()
if err != nil {
log.Fatal(err)
}
}
does anyone have any ideas? It starts running ffmpeg but just stalls.
Running stdinfill in a goroutine fixed the issue.
I have a function like this:
package main
import (
"fmt"
)
// PrintSomething prints some thing
func PrintSomething() {
fmt.Println("print something")
}
func main() {
PrintSomething()
}
How do I wrap PrintSomething to another function call CaptureSomething to save the string "print something" to a variable and return it?
Create pipe and set stdout to the pipe writer. Start a goroutine to copy the pipe reader to a buffer. When done, close the pipe writer and wait for goroutine to complete reading. Return the buffer as a string.
// capture replaces os.Stdout with a writer that buffers any data written
// to os.Stdout. Call the returned function to cleanup and get the data
// as a string.
func capture() func() (string, error) {
r, w, err := os.Pipe()
if err != nil {
panic(err)
}
done := make(chan error, 1)
save := os.Stdout
os.Stdout = w
var buf strings.Builder
go func() {
_, err := io.Copy(&buf, r)
r.Close()
done <- err
}()
return func() (string, error) {
os.Stdout = save
w.Close()
err := <-done
return buf.String(), err
}
}
Use it like this:
done := capture()
fmt.Println("Hello, playground")
capturedOutput, err := done()
if err != nil {
// handle error
}
playground example
For example,
package main
import (
"fmt"
"io/ioutil"
"os"
)
// PrintSomething prints some thing
func PrintSomething() {
fmt.Println("print something")
}
func CaptureSomething() (string, error) {
defer func(stdout *os.File) {
os.Stdout = stdout
}(os.Stdout)
out, err := ioutil.TempFile("", "stdout")
if err != nil {
return "", err
}
defer out.Close()
outname := out.Name()
os.Stdout = out
PrintSomething()
err = out.Close()
if err != nil {
return "", err
}
data, err := ioutil.ReadFile(outname)
if err != nil {
return "", err
}
os.Remove(outname)
return string(data), nil
}
func main() {
s, err := CaptureSomething()
if err != nil {
fmt.Println(err)
} else {
fmt.Print(s)
}
}
Playground: https://play.golang.org/p/O2kSegxYeGy
Output:
print something
Use one of these, whichever works for you:
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
)
func PrintSomething() {
fmt.Println("print something")
}
func PrintSomethingBig() {
for i := 0; i < 100000; i++ {
fmt.Println("print something")
}
}
func PrintSomethingOut(out io.Writer) {
fmt.Fprintln(out, "print something to io.Writer")
}
func PrintSomethingString() string {
return fmt.Sprintln("print something into a string")
}
// not thread safe
// modified by zlynx#acm.org from original at http://craigwickesser.com/2015/01/capture-stdout-in-go/
func captureStdout(f func()) string {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
go func() {
f()
w.Close()
}()
buf := &bytes.Buffer{}
// Will complete when the goroutine calls w.Close()
io.Copy(buf, r)
// Clean up.
os.Stdout = old
r.Close()
return buf.String()
}
func main() {
str1 := &strings.Builder{}
str2 := PrintSomethingString()
PrintSomethingOut(str1)
PrintSomethingOut(os.Stdout)
str3 := captureStdout(PrintSomething)
str4 := captureStdout(PrintSomethingBig)
fmt.Println("string 1 is", str1)
fmt.Println("string 2 is", str2)
fmt.Println("string 3 is", str3)
fmt.Println("string 4 len", len(str4))
}
I'm trying print to stdout from golang using $PAGER or manually invoking more or less to allow the user to easily scroll through a lot of options. How can I achieve this?
You can use the os/exec package to start a process that runs less (or whatever is in $PAGER) and then pipe a string to its standard input. The following worked for me:
func main() {
// Could read $PAGER rather than hardcoding the path.
cmd := exec.Command("/usr/bin/less")
// Feed it with the string you want to display.
cmd.Stdin = strings.NewReader("The text you want to show.")
// This is crucial - otherwise it will write to a null device.
cmd.Stdout = os.Stdout
// Fork off a process and wait for it to terminate.
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
I assume you already have output being printed from your program to stdout that you want to have captured and sent to your pager, you don't want to
rewrite the I/O to use another input stream like the other responses require.
You can create an os.Pipe which works the same as running a command with "|less" by attaching one side to your pager and the other side to stdout like this:
// Create a pipe for a pager to use
r, w, err := os.Pipe()
if err != nil {
panic("You probably want to fail more gracefully than this")
}
// Capture STDOUT for the Pager. Keep the old
// value so we can restore it later.
stdout := os.Stdout
os.Stdout = w
// Create the pager process to execute and attach
// the appropriate I/O streams.
pager := exec.Command("less")
pager.Stdin = r
pager.Stdout = stdout // the pager uses the original stdout, not the pipe
pager.Stderr = os.Stderr
// Defer a function that closes the pipe and invokes
// the pager, then restores os.Stdout after this function
// returns and we've finished capturing output.
//
// Note that it's very important that the pipe is closed,
// so that EOF is sent to the pager, otherwise weird things
// will happen.
defer func() {
// Close the pipe
w.Close()
// Run the pager
if err := pager.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
// restore stdout
os.Stdout = stdout
}()
Here is a somewhat naive cat example that uses $PAGER when set.
package main
import (
"io"
"log"
"os"
"os/exec"
)
func main() {
var out io.WriteCloser
var cmd *exec.Cmd
if len(os.Args) != 2 {
log.Fatal("Wrong number of args: gcat <file>")
}
fileName := os.Args[1]
file, err := os.Open(fileName)
if err != nil {
log.Fatal("Error opening file: ", err)
}
pager := os.Getenv("PAGER")
if pager != "" {
cmd = exec.Command(pager)
var err error
out, err = cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
if err := cmd.Start(); err != nil {
log.Fatal("Unable to start $PAGER: ", err)
}
} else {
out = os.Stdout
}
_, err = io.Copy(out, file)
if err != nil {
log.Fatal(err)
}
file.Close()
out.Close()
if cmd != nil {
if err := cmd.Wait(); err != nil {
log.Fatal("Error waiting for cmd: ", err)
}
}
}
This version creates an io.Writer called pager for all the output that you want paged (you can assign it to os.Stdout if you like) and correctly closes that and waits for the $PAGER when main() returns.
import (
"fmt"
"io"
"log"
"os"
"os/exec"
)
var pager io.WriteCloser
func main() {
var cmd *exec.Cmd
cmd, pager = runPager()
defer func() {
pager.Close()
cmd.Wait()
}()
fmt.Fprintln(pager, "Hello, 世界")
}
func runPager() (*exec.Cmd, io.WriteCloser) {
pager := os.Getenv("PAGER")
if pager == "" {
pager = "more"
}
cmd := exec.Command(pager)
out, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
return cmd, out
}
How can I pipe several external commands together in Go? I've tried this code but I get an error that says exit status 1.
package main
import (
"io"
"log"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
stdout1, err := c1.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err = c1.Start(); err != nil {
log.Fatal(err)
}
if err = c1.Wait(); err != nil {
log.Fatal(err)
}
c2 := exec.Command("wc", "-l")
c2.Stdin = stdout1
stdout2, err := c2.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err = c2.Start(); err != nil {
log.Fatal(err)
}
if err = c2.Wait(); err != nil {
log.Fatal(err)
}
io.Copy(os.Stdout, stdout2)
}
For simple scenarios, you could use this approach:
bash -c "echo 'your command goes here'"
For example, this function retrieves the CPU model name using piped commands:
func getCPUmodel() string {
cmd := "cat /proc/cpuinfo | egrep '^model name' | uniq | awk '{print substr($0, index($0,$4))}'"
out, err := exec.Command("bash","-c",cmd).Output()
if err != nil {
return fmt.Sprintf("Failed to execute command: %s", cmd)
}
return string(out)
}
StdoutPipe returns a pipe that will be connected to the command's
standard output when the command starts. The pipe will be closed
automatically after Wait sees the command exit.
(from http://golang.org/pkg/os/exec/#Cmd.StdinPipe )
The fact you do c1.Wait closes the stdoutPipe.
I made a working example (just a demo, add error catching!) :
package main
import (
"bytes"
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
r, w := io.Pipe()
c1.Stdout = w
c2.Stdin = r
var b2 bytes.Buffer
c2.Stdout = &b2
c1.Start()
c2.Start()
c1.Wait()
w.Close()
c2.Wait()
io.Copy(os.Stdout, &b2)
}
package main
import (
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
_ = c1.Run()
_ = c2.Wait()
}
Like the first answer but with the first command started and waited for in a goroutine. This keeps the pipe happy.
package main
import (
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
pr, pw := io.Pipe()
c1.Stdout = pw
c2.Stdin = pr
c2.Stdout = os.Stdout
c1.Start()
c2.Start()
go func() {
defer pw.Close()
c1.Wait()
}()
c2.Wait()
}
This is a fully working example. The Execute function takes any number of exec.Cmd instances (using a variadic function) and then loops over them correctly attaching the output of stdout to the stdin of the next command. This must be done before any function is called.
The call function then goes about calling the commands in a loop, using defers to call recursively and ensuring proper closure of pipes
package main
import (
"bytes"
"io"
"log"
"os"
"os/exec"
)
func Execute(output_buffer *bytes.Buffer, stack ...*exec.Cmd) (err error) {
var error_buffer bytes.Buffer
pipe_stack := make([]*io.PipeWriter, len(stack)-1)
i := 0
for ; i < len(stack)-1; i++ {
stdin_pipe, stdout_pipe := io.Pipe()
stack[i].Stdout = stdout_pipe
stack[i].Stderr = &error_buffer
stack[i+1].Stdin = stdin_pipe
pipe_stack[i] = stdout_pipe
}
stack[i].Stdout = output_buffer
stack[i].Stderr = &error_buffer
if err := call(stack, pipe_stack); err != nil {
log.Fatalln(string(error_buffer.Bytes()), err)
}
return err
}
func call(stack []*exec.Cmd, pipes []*io.PipeWriter) (err error) {
if stack[0].Process == nil {
if err = stack[0].Start(); err != nil {
return err
}
}
if len(stack) > 1 {
if err = stack[1].Start(); err != nil {
return err
}
defer func() {
if err == nil {
pipes[0].Close()
err = call(stack[1:], pipes[1:])
}
}()
}
return stack[0].Wait()
}
func main() {
var b bytes.Buffer
if err := Execute(&b,
exec.Command("ls", "/Users/tyndyll/Downloads"),
exec.Command("grep", "as"),
exec.Command("sort", "-r"),
); err != nil {
log.Fatalln(err)
}
io.Copy(os.Stdout, &b)
}
Available in this gist
https://gist.github.com/tyndyll/89fbb2c2273f83a074dc
A good point to know is that shell variables like ~ are not interpolated
I wanted to pipe some video and audio to FFplay. This worked for me:
package main
import (
"io"
"os/exec"
)
func main() {
ffmpeg := exec.Command(
"ffmpeg", "-i", "247.webm", "-i", "251.webm", "-c", "copy", "-f", "webm", "-",
)
ffplay := exec.Command("ffplay", "-")
ffplay.Stdin, ffmpeg.Stdout = io.Pipe()
ffmpeg.Start()
ffplay.Run()
}
https://golang.org/pkg/io#Pipe
package main
import (
...
pipe "github.com/b4b4r07/go-pipe"
)
func main() {
var b bytes.Buffer
pipe.Command(&b,
exec.Command("ls", "/Users/b4b4r07/Downloads"),
exec.Command("grep", "Vim"),
)
io.Copy(os.Stdout, &b)
}
I spent a good day trying to use Denys Séguret answer to come up with a wrapper for multiple exec.Command before I came across this neat package by b4b4r07.
Because it can be complex to build such command chains I have decided to implements a litte go library for that purpose: https://github.com/rainu/go-command-chain
package main
import (
"bytes"
"fmt"
"github.com/rainu/go-command-chain"
)
func main() {
output := &bytes.Buffer{}
err := cmdchain.Builder().
Join("ls").
Join("wc", "-l").
Finalize().WithOutput(output).Run()
if err != nil {
panic(err)
}
fmt.Printf("Errors found: %s", output)
}
With the help of this lib you can also configure std-error forwarding and other things.