I started learning and playing around with Go to see what it is like to make some more complex console/cli type tools instead of using shells or Python. I want to execute commands and display the output. I figured out how to print the output like this:
out, err := exec.Command("pwd").Output()
print(string(out))
Is there a way to execute the commands and have it default to stdout like a shell script, or do I need to make a helper function for this?
Update: After getting IntelliJ and the Go plugin, I poked around in the Go source and agree there is currently no way to do with without a helper method.
It is not possible to reuse a Cmd object as per this comment in the exec.go source code:
// A Cmd cannot be reused after calling its Run, Output or CombinedOutput
// methods.
I did incorporate the stdout option into my own helper, including other options like shell integration. I will try turn that into open source if I can make it useful. An interesting first day of Go.
The solution
Actually, it is pretty easy. You can set the stdout of the command to os.Stdout and Bob's your uncle:
package main
import (
"os"
"os/exec"
)
func main() {
cmd := exec.Command("pwd")
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
panic(err)
}
}
What's happening here?
By default, the output of a command is stored in a bytes.Buffer if cmd.Stdout is not set to another io.Writer. The call of cmd.Output() then runs the command and saves the output to said buffer.
Since os.Stdout implements io.Writer interface, we simply set cmd.Stdout to be os.Stdout. Now when .Run() is called, the output of the command gets written to the io.Writer defined in cmd.Stdout, which happens to be os.Stdout and the output gets written in the shell.
EDIT: As per comment, if all commands should write to os.Stdout, there of course is no way to prevent some helper. I'd do it like this:
package main
import (
"os"
"os/exec"
)
func CmdToStdout( c string ) (err error){
cmd := exec.Command(c)
cmd.Stdout = os.Stdout
err = cmd.Run()
return
}
func main() {
err := CmdToStdout("pwd")
if err != nil {
panic(err)
}
}
You have to create a helper if you need this often (and 5 lines looks too much). Based on the documentation this is a recommended way:
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)
}
Related
There are certain fmt.Print statements that I want to save into a .txt file.
I don't want to store all print statments. Can I do this ?
package main
import (
"fmt"
"io"
"log"
"os"
)
func main() {
file, err := os.Create("myfile")
if err != nil {
log.Fatal(err)
}
mw := io.MultiWriter(os.Stdout, file)
fmt.Fprintln(mw, "This line will be written to stdout and also to a file")
}
Use the fmt.Fprint() method for calls you want to save to a file. There are also fmt.Fprintf() and fmt.Fprintln().
These functions take a destination io.Writer as the first argument, to which you can pass your file (*os.File).
For example:
f, err := os.Open("data.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
fmt.Println("This goes to standard output.")
fmt.Fprintln(f, "And this goes to the file")
fmt.Fprintf(f, "Also to file, with some formatting. Time: %v, line: %d\n",
time.Now(), 2)
If you want all fmt.PrintXX() calls to go to the file which you have no control over (e.g. you can't change them to fmt.FprintXX() because they are part of another library), you may change os.Stdout temporarily, so all further fmt.PrintXX() calls will write to the output you set, e.g.:
// Temporarily set your file as the standard output (and save the old)
old, os.Stdout = os.Stdout, f
// Now all fmt.PrintXX() calls output to f
somelib.DoSomething()
// Restore original standard output
os.Stdout = old
I'm trying to use Go's os/exec Command() to simulate a keypress, and sometimes I'll want to use this keypress multiple times in quick succession. I'm using exec.Command to call "xte", "key XF86AudioPlay", which pauses music on a Linux OS. While the Command can Start() or Run() no problem, if I try to execute again, I get an error:
exec: already started
I've tried using Process.Kill() immediately after the execution, in order to free it up, but this will make the execution not work in the first place. I got this idea from here: Terminating a Process Started with os/exec in Golang
My code uses a switch and calls this pause function accordingly, but I'll simply share the basis of the code I wrote, with the case as an example function:
cmd := exec.Command("xte", "key XF86AudioPlay")
//...
func Pause() {
err := cmd.Start() // or cmd.Run()
if err != nil {
fmt.Println(err)
}
err = cmd.Process.Kill()
if err != nil {
fmt.Printf("Failed to kill: %s", err)
}
}
So, to recap, I'm successful at calling it one time, but upon success calls to Pause(), I get the error from cmd.Start()/Run() which read: exec: already started.
I also tried going lower, that is, using syscall, but I ran into some trouble. I tried:
args[0] = "xte"
args[1] = "key"
args[2] = "XF86AudioPlay"
//... Pause():
err := syscall.Exec("/bin", args, os.Environ())
if err != nil {
fmt.Println(err)
}
And here I got a permission denied error, even running as super user (sudo).
How should I proceed call this Command() and then free it up for immediate recall? Or am I on the right track with syscall?
Edit
So the solution as both Amd and Son Bui state, was to create the Command everytime I intend to call it, basically putting the assignment cmd := exec.Command()inside my Pause() method.
As the type Cmd struct { Docs said:
A Cmd cannot be reused after calling its Run, Output or CombinedOutput
methods.
1- Use two separate exec.Command like this,
Also you may need some delay between runs (simulating two separate keystrokes):
package main
import (
"fmt"
"os"
"os/exec"
"time"
)
func main() {
Pause()
fmt.Println("Once")
time.Sleep(100 * time.Millisecond)
Pause()
fmt.Println("Twice")
}
func Pause() {
cmd := exec.Command("xte", "key XF86AudioPlay")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
}
2- You may run it once:
You may use something like this (not tested):
xte 'key XF86AudioPlay' 'key XF86AudioPlay'
and consider adding a short delay to the xte command (simulating two separate keystrokes):
xte 'key XF86AudioPlay' 'usleep 100000' 'key XF86AudioPlay'
Like this:
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
Pause()
fmt.Println("Once")
}
func Pause() {
cmd := exec.Command("xte", `key XF86AudioPlay`, `usleep 100000`, `key XF86AudioPlay`)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
}
See:
https://askubuntu.com/questions/499926/why-do-these-xte-commands-work-in-terminal-but-not-when-bound-with-xbindkeys
http://manpages.ubuntu.com/manpages/wily/man1/xte.1.html
http://wiki.robotz.com/index.php/Linux_Tools_to_Remap_Keys_and_Mouse_Buttons
I hope this helps.
See from source code:
cmd struct: (https://golang.org/src/os/exec/exec.go line 99)
// Process is the underlying process, once started.
Process *os.Process
And in Start function (https://golang.org/src/os/exec/exec.go line 327)
:
if c.Process != nil {
return errors.New("exec: already started")
}
So you can only use cmd.Start once time. If you want to use multiple time, you can create new Cmd OR run multiple command in once, ex:
cmd := exec.Command("/bin/sh", "-c", "command1; command2; command3; ...")
Let's say I have a program than outputs things on file descriptor 3; something like this:
package main
import "os"
func main() {
fd3 := os.NewFile(3, "fd3")
fd3.Write([]byte("FOOBAR\n"))
fd3.Close()
}
Now, I want to get the output sent to file descriptor 3 from a Go program:
package main
import (
"bufio"
"fmt"
"os/exec"
"os"
)
func main() {
cmd := exec.Command("./client")
cmd.Stderr = os.Stderr
fd3 := os.NewFile(3, "fd3")
defer fd3.Close()
cmd.ExtraFiles = []*os.File{fd3}
err := cmd.Start()
if err != nil {
panic(err)
}
go func() {
for {
reader := bufio.NewReader(fd3)
line, err := reader.ReadString('\n')
if err != nil {
panic(err)
}
fmt.Print(line)
}
}()
cmd.Wait()
fmt.Println("--- END ---")
}
But that does not work as it outputs the following error:
panic: read fd3: bad file descriptor
I don't understand what's wrong with my code. Anyone willing to help?
os.NewFile doesn't actually open a file descriptor. It's really an API to wrap a fd that was given to you.
look at the godoc: http://golang.org/pkg/os/#Create
(click the name Create, which currently points to this)
I think you want to call os.Create(name) and pass the fd to the child process
or potentiall os.Open / os.OpenFile if you need to set mode and stuff
I've got a command line tool written in Golang and I need to start vim from it. However it's not working, and there's not any error or much else to work with. I've reduced the code to just this:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("vim", "test.txt")
err := cmd.Run()
fmt.Println(err)
}
When I run this, I can see the vim process for a 2-3 seconds but the application doesn't actually open. Then the program simply exits (and the vim process closes) with an "exit status 1".
I've also tried this to capture stderr:
package main
import (
"bytes"
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("vim", "test.txt")
var stderr bytes.Buffer
cmd.Stderr = &stderr
err := cmd.Run()
fmt.Println(err)
fmt.Println(stderr)
}
But in this case, the program gets stuck indefinitely.
Any idea what could be the issue?
Pass on stdin and stdout from the calling program which, provided it was run from a terminal (likely for a command line program) will start vim for you and return control when the user has finished editing the file.
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("vim", "test.txt")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err := cmd.Run()
fmt.Println(err)
}
VIM needs a proper terminal and detects the absence of one.
If you use the StderrPipe and read it while vim is running you will see this:
2014/02/02 20:25:49 Vim: Warning: Output is not to a terminal
2014/02/02 20:25:49 Vim: Warning: Input is not from a terminal
Example for reading stderr while executing (on play):
func logger(pipe io.ReadCloser) {
reader := bufio.NewReader(pipe)
for {
output, err := reader.ReadString('\n')
if err != nil {
log.Println(err)
return
}
log.Print(string(output))
}
}
pipe, err := cmd.StderrPipe()
go logger(pipe)
cmd.Run()
For vim to run you probably need to emulate a terminal.
Maybe goat (doc) can help you out:
tty := term.NewTTY(os.Stdin)
cmd := exec.Command("vim", "test.txt")
cmd.Stdin = t
cmd.Stdout = t
// ...
I'm writing a program in Go that executes a server like program (also Go). Now I want to have the stdout of the child program in my terminal window where I started the parent program. One way to do this is with the cmd.Output() function, but this prints the stdout only after the process has exited. (That's a problem because this server-like program runs for a long time and I want to read the log output)
The variable out is of type io.ReadCloser and I don't know what I should do with it to achieve my task, and I can't find anything helpful on the web on this topic.
func main() {
cmd := exec.Command("/path/to/my/child/program")
out, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
}
err = cmd.Start()
if err != nil {
fmt.Println(err)
}
//fmt.Println(out)
cmd.Wait()
}
Explanation to the code: uncomment the Println function to get the code to compile, I know that Println(out io.ReadCloser) is not a meaningful function.
(it produces the output &{3 |0 <nil> 0} ) These two lines are just required to get the code to compile.
Now I want to have the stdout of the child program in my terminal
window where I started the parent program.
No need to mess with pipes or goroutines, this one is easy.
func main() {
// Replace `ls` (and its arguments) with something more interesting
cmd := exec.Command("ls", "-l")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()
}
I believe that if you import io and os and replace this:
//fmt.Println(out)
with this:
go io.Copy(os.Stdout, out)
(see documentation for io.Copy and for os.Stdout), it will do what you want. (Disclaimer: not tested.)
By the way, you'll probably want to capture standard-error as well, by using the same approach as for standard-output, but with cmd.StderrPipe and os.Stderr.
For those who don't need this in a loop, but would like the command output to echo into the terminal without having cmd.Wait() blocking other statements:
package main
import (
"fmt"
"io"
"log"
"os"
"os/exec"
)
func checkError(err error) {
if err != nil {
log.Fatalf("Error: %s", err)
}
}
func main() {
// Replace `ls` (and its arguments) with something more interesting
cmd := exec.Command("ls", "-l")
// Create stdout, stderr streams of type io.Reader
stdout, err := cmd.StdoutPipe()
checkError(err)
stderr, err := cmd.StderrPipe()
checkError(err)
// Start command
err = cmd.Start()
checkError(err)
// Don't let main() exit before our command has finished running
defer cmd.Wait() // Doesn't block
// Non-blockingly echo command output to terminal
go io.Copy(os.Stdout, stdout)
go io.Copy(os.Stderr, stderr)
// I love Go's trivial concurrency :-D
fmt.Printf("Do other stuff here! No need to wait.\n\n")
}