How open other tools like htop, vim by os's package - go

I'm writing a new project like a CLI with Go and I'm using the package termui, but in a time, I need that CLI open a file with editor like VIM without exit the current CLI, when close the VIM I can back to current CLI. Is it possible?
I've tried with this example below:
package main
import (
"log"
"os/exec"
)
func main() {
// render the termui
path := "SomeFile.bash"
cmd := exec.Command("vim", path)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}

I did resolve with instance of os like os.Stdin or os.Stdout
Example:
package main
import (
"log"
"os/exec"
)
func main() {
// render the termui
path := "SomeFile.bash"
cmd := exec.Command("vim", path)
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
Thanks #Peter by commentary :)

Related

GO attach stdin in test setup method

I am using the built-in testing module to run some functional tests I have in my GO project. In my project I have external dependencies, which I connect to in my TestMain method. I save these connections to variables which then I use in the tests themselves, and the connections can take a long time to establish properly (Kafka anyone?). I would like to run the tests on-demand, but after these variables have been setup.
So what I want is to listen to stdin in my TestMain function and either run or quit the tests. But I want it to be controlled by the user so I can have my test environment setup, and the tests will run on my command.
But sadly, it seems that when running go test ... that stdin is mapped directly to /dev/null. So when I try to read os.Stdin I get an EOF error. The minimum code for this is:
package tests
import (
"bufio"
"fmt"
"os"
"testing"
)
func TestMain(m *testing.M) {
reader := bufio.NewReader(os.Stdin)
if input, err := reader.ReadString('\n'); err != nil {
fmt.Println(err)
fmt.Println("-----------")
fmt.Println(input)
os.Exit(1)
}
fmt.Println("ESCAPED!")
os.Exit(m.Run())
}
I have read in circles how to mock this for unit tests and the sort, but my case is more of a functional test runner. Is there a way, or even some trickery, that will allow me to open or change the test processes stdin?
You can redirect os.Stdin, it depends on OS though:
package tests
import (
"fmt"
"os"
"testing"
"bufio"
"runtime"
)
func TestMain(m *testing.M) {
var ttyName string
if runtime.GOOS == "windows" {
ttyName = "con"
} else {
ttyName = "/dev/tty"
}
f, err := os.Open(ttyName)
if err != nil {
panic(err)
}
defer f.Close()
oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }()
os.Stdin = f
reader := bufio.NewReader(os.Stdin)
if input, err := reader.ReadString('\n'); err != nil {
fmt.Println("Error:", err)
fmt.Println("-----------")
fmt.Println(input)
os.Exit(1)
}
fmt.Println("ESCAPED!")
os.Exit(m.Run())
}
func TestHello(t *testing.T){
fmt.Println("Hello")
}

How to get the output of a command

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

How to stop fmt.print() from printing to the output?

I'm using a package which has some fmt.print()s in the code, and I want to stop them from printing to the output. I want to suppress them without changing the code inside the package and only by adding some lines to my main.go.
Is it possible to force fmt not to log prints to the output?
Yes, just divert os.Stdout and/or os.Stderr e.g.:
package main
import (
"fmt"
"os"
)
func main() {
temp := os.Stdout
os.Stdout = nil // turn it off
packageFunctions() // call you package functions here
os.Stdout = temp // restore it
fmt.Println("Bye")
}
func packageFunctions() {
fmt.Println("Hi")
}
Output:
Bye
You may divert it to a temp file:
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
)
func main() {
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
log.Fatal(err)
}
fmt.Println(tmpfile.Name())
// defer os.Remove(tmpfile.Name()) // clean up
temp := os.Stdout
os.Stdout = tmpfile
packageFunctions() // call you package functions here
if err := tmpfile.Close(); err != nil {
log.Fatal(err)
}
os.Stdout = temp // restore it
fmt.Println("Bye")
}
func packageFunctions() {
fmt.Println("Hi")
}
And see:
How to copy os.Stdout output to string variable
How can stdout be captured or suppressed for Go(lang) testing?

golang exec.Command read std input

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
`)
}

how do i open file with application like emacs,vim, TextMate in golang?

How do I open a file within an application like emacs, vim, TextMate in golang?
cmd := exec.Command("open", "-a", "/usr/bin/vim", "/var/www/index.php")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
or
cmd := exec.Command("/usr/bin/vim", "/var/www/index.php")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
I found both of the above cannot work, is there any good solution?
This definitely works - what errors are you getting ?
package main
import (
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("/usr/bin/vim", "/var/www/index.php")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
}

Resources