Command.Start() Causes Program To Hang - windows

I'm trying to open a new application, and then close the current application. I read that Command.Start() doesn't wait for the command to finish, but my program doesn't exit until fgl-updater.exe exits as well. I need it to close so that I can remove/replace it with the updated version.
func RunUpdater() {
wd, _ := os.Getwd()
cmd := exec.Command("cmd.exe", "/c", "start", wd+"/fgl-updater.exe")
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
err := cmd.Start()
if err != nil {
fmt.Println("ERROR: ", err)
}
os.Exit(0)
}
Is this a Windows/cmd issue?

Related

stream file that is currently being written to

Is it possible to, in a goroutine, stream a file as it is being written to by a subprocess command? The goal here is to capture the output as both a file and stream it live. I have:
cmd := exec.CommandContext(ctx, c.Bin, args...)
// CANT USE NON FILE!!
// https://github.com/golang/go/issues/23019
tempout, err := ioutil.TempFile("", "workerout")
if err != nil {
return "", err
}
tempoutName := tempout.Name()
defer os.Remove(tempoutName) // clean up
temperr, err := ioutil.TempFile("", "workererr")
if err != nil {
return "", err
}
temperrName := temperr.Name()
defer os.Remove(temperrName) // clean up
cmd.Stdout = tempout
cmd.Stderr = temperr
err = cmd.Start()
// Stream the logs
// Does not work. Flushing issue???
/*
ro := bufio.NewReader(tempout)
go func() {
line, _, _ := ro.ReadLine()
logger.Debug(line)
}()
re := bufio.NewReader(temperr)
go func() {
line, _, _ := re.ReadLine()
logger.Error(line)
}()
*/
cmd.Wait()
return tempout.Read(... // read the file into a string and return it
The commented out section of the code seems to show the logs only once the command exits (either by ctx being cancelled, or it finishes), in that it does not log in real time. Is there a way to make this log in real time?

How to open a process and record stdin and stdout properly in Go?

I've been trying to write a program that record what is passed to a subprocess and the console returns in live (in the future, to record SSH sessions, for now on Python shell for testing)
I can record without issue stdout and stderr (it shows and record it correctly) but I can't find a way to do the same on stdin ?
Basically that my stdin will both map to the subprocess stdin and write to the log file.
There is my current code :
func SSH(cmd *cobra.Command, args []string) {
logFile := fmt.Sprintf("%v#%s.log", args[0], time.Now().Format(SSHLogDateFormat))
usr, _ := user.Current()
home := usr.HomeDir
logDir := fmt.Sprintf("%s/%s/logs", home, config.ConfigDir)
if _, err := os.Stat(logDir); os.IsNotExist(err) {
err = os.Mkdir(logDir, os.FileMode(int(0700)))
if err != nil {
log.Fatalf("Failed to create %s: %s", logDir, err)
}
}
fullLogFile := fmt.Sprintf("%s/%s", logDir, logFile)
log.Infof("Started recording to %s", fullLogFile)
bash, err := exec.LookPath("bash")
if err != nil {
log.Errorf("Could not locate bash: %v", err)
}
f, err := os.Create(fullLogFile)
if err != nil {
log.Fatalf("Failed to open device logs: %s", err)
}
command := exec.Command(bash, "-c", "python")
out := io.MultiWriter(os.Stdout, f)
command.Stderr = out
command.Stdout = out
if err := command.Start(); nil != err {
log.Fatalf("Error starting program: %s, %s", command.Path, err.Error())
}
err = command.Wait()
if err != nil {
log.Fatalf("Error waiting program: %s, %s", command.Path, err.Error())
}
f.Close()
log.Infof("Finished recording to %s", fullLogFile)
}
Tried this too without success :
out := io.MultiWriter(os.Stdout, f)
in := io.TeeReader(os.Stdin, out)
command.Stderr = out
command.Stdout = out
command.Stdin = in
You need to write to the process's stdin. Get a write pipe to that:
procIn, err := command.StdinPipe()
if nil!=err {
log.Fatal(err)
}
Then create a multiWriter to write to both log and process:
inWriter := io.MultiWriter(procIn, f)
Finally, copy Stdin into the MultiWriter:
go func() {
io.Copy(inWriter, os.Stdin)
procIn.Close()
}()
We do the copy in a goroutine, so as not to hang everything up: we haven't started the command yet, so there's nothing receiving the written bytes. It needs to occur in parallel to the command running.
Here's a very simple example:
package main
import (
`os`
`os/exec`
`io`
)
// pipeto copies stdin to logOut and to the command,
// and copies the commands stdout and stderr to logOut and
// to our stderr.
func pipeto(logOut os.Writer, cmd string, args ...string) error {
cmd := exec.Command(cmd, args...)
out := io.MultiWriter(os.Stdout, logOut)
cmd.Stderr, cmd.Stdout = out, out
procIn, err := cmd.StdinPipe()
if nil!=err {
return err
}
go func() {
io.Copy( io.MultiWriter(procIn, logOut) , os.Stdin )
procIn.Close()
}()
return cmd.Run()
}
func main() {
logOut, err := os.Create(`logout.log`)
if nil!=err {
panic(err)
}
defer logOut.Close()
if err := pipeto(logOut, `sed`, `s/tea/coffee/g`); nil!=err {
panic(err)
}
}
You can test it, where I've named my go file pipetest.go:
echo this is a test of tea | go run pipetest.go
The you will see both the input and the output reflected in logout.log:
this is a test of tea
this is a test of coffee
At the end I found the solution by using the PTY library (That would have been needed anyway to handle special signals and tabs on subprocesses): https://github.com/creack/pty
I took the Shell example and just replaced the io.Copy with my MultiWriter

I need to copy the input given by the user to the child Process. How can I copy the input given to child processes stdIn?

I need to copy all the input provided by the user for the child process during its execution. I have tried to scan the cmd.Stdin for the copy of input but can't get it. Am I missing something here?
func main(){
cmd:= exec.Command("python", "-i")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
buff := bufio.NewScanner(cmd.Stdin)
go func(){
for buff.Scan(){
fmt.Println(buff.Text())
}
}()
_ = cmd.Run()
}
I think you'll actually need to capture the input, and pass it to the subprocess...
func main(){
cmd := exec.Command("python", "-i")
stdin, err := cmd.StdinPipe()
if err != nil {
panic(err)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
buff := bufio.NewScanner(os.Stdin)
go func(){
for buff.Scan(){
input := buff.Text()
fmt.Println(input)
io.WriteString(stdin, input)
}
}()
cmd.Start()
cmd.Wait()
}

Using sftp << INPUT via golang os/exec

What I want to do is exporting a file from my server via SFTP, in golang.
Here is the troubling code
cmd = exec.Command("sftp", "login#sftp.com", `INPUT
cd /some/path
put file.gz
quit
INPUT`)
cmd.Stderr = &stderr
err = cmd.Run()
if err != nil {
fmt.Println(stderr.String())
os.Exit(1)
}
fmt.Println("done")
It gets to done but doesn't import file.gz.
I finally found the solution.
cmd = exec.Command("sftp", "login#sftp.com")
cmd.Stdin = strings.NewReader(`cd some/path
put file.gz
quit`)
cmd.Stderr = &stderr
err = cmd.Start()
if err != nil {
fmt.Println(stderr.String())
fmt.Println(err)
os.Exit(1)
}
err = cmd.Wait()
I set the commands I need to do once I'm in the STFP by setting cmd.Stdin
cmd.Start() and cmd.Wait() starts the command and stops when the whole command is done.

Echo command in Golang

I'm currently trying to execute a simple echo command in Golang on Linux. My code is the following:
cmd = exec.Command("echo", "\"foo 0x50\"", ">", "test.txt")
_, err = cmd.Output()
if err != nil {
fmt.Println(err)
}
But test.txt doesn't appear in my folder (even after compile and run the code). That not the first that that I use this method to execute commands and I never thought that I will be block on an echo command.
So how can I fix this code in order to have "foo 0x50" (with the quotes) in the test.txt?
You can redirect the stdout like this:
// Remove the redirect from command
cmd := exec.Command("echo", "\"foo 0x50\"")
// Make test file
testFile, err := os.Create("test.txt")
if err != nil {
panic(err)
}
defer outfile.Close()
// Redirect the output here (this is the key part)
cmd.Stdout = testFile
err = cmd.Start(); if err != nil {
panic(err)
}
cmd.Wait()
For anyone who wants to pass the complete command in oneline/string ,
func call(command string) {
cmd := exec.Command("sudo", "bash", "-c", command)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
fmt.Println(err)
}
err1 := cmd.Wait()
if err1 != nil {
fmt.Println(err1)
}
}
The Wait waits for the command to exit and waits for any copying to stdin or copying from stdout or stderr to complete. It closes the pipe after seeing the command exit.
Sources :
https://stackoverflow.com/a/43246464/9892358
https://stackoverflow.com/a/43246464/9892358
https://zetcode.com/golang/exec-command/

Resources