How to run a simple Windows command?
This command:
exec.Command("del", "c:\\aaa.txt")
.. outputs this message:
del: executable file not found in %path%
What am I doing wrong?
I got the same error as you.
But dystroy is correct: You can't run del or any other command built into cmd because there is no del.exe file (or any other del-executable for that matter).
I got it to work with:
package main
import(
"fmt"
"os/exec"
)
func main(){
c := exec.Command("cmd", "/C", "del", "D:\\a.txt")
if err := c.Run(); err != nil {
fmt.Println("Error: ", err)
}
}
You need a Windows cmd to execute your dir command.
Try this :
cmd := exec.Command("cmd", "/C", "dir").Output()
(sorry, no Windows computer to check it right now)
Found another solution too. Create a batch file that contains the following: del c:\aaa.txt
Then call it like this:
exec.Command("c:\\del.bat").Run()
In case you need the output of cmd:
if c, err := exec.Command("cmd","/c","del","a.txt").CombinedOutput(); err != nil {
log.Fatal(err)
} else {
fmt.Printf("%s\n", c)
}
Ok let's see, according to the documentation, in windows, processes receive commands as a single line string and do some parsing of their own. Exec's Command function builds the command string by combining all arguments together using CommandLineToArgvW, that despite being the most common quoting algorithm doesn't work for every application. Applications like msiexec.exe and cmd.exe use an incompatible unquoting algorithm, hence the extra mile.
Heres a different example using powershell
package main
import (
"os/exec"
"fmt"
"log"
)
func main() {
out, err := exec.Command("powershell","remove-item","aaa.txt").Output()
if err != nil {
log.Fatal(err)
} else {
fmt.Printf("%s",out)
}
you can try use github.com/go-cmd/cmd module.
because golang can not use syscall by default.
example:
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
}
c := exec.Command("cmd", "/C", "dir", "d:\\")
c.Stdin = os.Stdin
c.Stdout = os.Stdout
c.Stderr = os.Stderr
c.Run()
Related
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; ...")
I want to source shell scripts using Go. Ideally the following code
cmd := exec.Command("/bin/bash", "source", file.Name())
but, I know that "source" is a bash built-in function, not an executable.
However, I have found some ways to mimic this behavior in Python:
http://pythonwise.blogspot.fr/2010/04/sourcing-shell-script.html
Unfortunately, I don't know how to translate this in Go. Does anyone have an idea ?
Thanks !
You can set environmental variables when running a program using exec:
cmd := exec.Command("whatever")
cmd.Env = []string{"A=B"}
cmd.Run()
If you really need source then you can run your command through bash:
cmd := exec.Command("bash", "-c", "source " + file.Name() + " ; echo 'hi'")
cmd.Run()
Check out this library for a more full-featured workflow: https://github.com/progrium/go-basher.
Update: Here's an example that modifies the current environment:
package main
import (
"bufio"
"bytes"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
)
func main() {
err := ioutil.WriteFile("example_source", []byte("export FOO=bar; echo $FOO"), 0777)
if err != nil {
log.Fatal(err)
}
cmd := exec.Command("bash", "-c", "source example_source ; echo '<<<ENVIRONMENT>>>' ; env")
bs, err := cmd.CombinedOutput()
if err != nil {
log.Fatalln(err)
}
s := bufio.NewScanner(bytes.NewReader(bs))
start := false
for s.Scan() {
if s.Text() == "<<<ENVIRONMENT>>>" {
start = true
} else if start {
kv := strings.SplitN(s.Text(), "=", 2)
if len(kv) == 2 {
os.Setenv(kv[0], kv[1])
}
}
}
}
log.Println(os.Getenv("FOO"))
I have recently added such a utility function to my shell/bash Golang library:
https://godoc.org/mvdan.cc/sh/shell#SourceFile
For example, you could do:
vars, err := shell.SourceFile("foo.sh")
if err != nil { ... }
fmt.Println(vars["URL"].Value)
// http://the.url/value
It's decently safe, because it never actually calls bash nor any other program. It parses the shell script, then interprets it. But when interpreting, it has a whitelist of what files the script can open and what programs the script can execute.
The interpreter also has a context.Context, so you can set a timeout if you want to be protected against forever loops or other bad code.
I have tried following the Go Docs in order to call a python script which just outputs "Hello" from GO, but have failed until now.
exec.Command("script.py")
or I've also tried calling a shell script which simply calls the python script, but also failed:
exec.Command("job.sh")
Any ideas how would I achieve this?
EDIT
I solved following the suggestion in the comments and adding the full path to exec.Command().
Did you try adding Run() or Output(), as in:
exec.Command("script.py").Run()
exec.Command("job.sh").Run()
You can see it used in "How to execute a simple Windows DOS command in Golang?" (for Windows, but the same idea applies for Unix)
c := exec.Command("job.sh")
if err := c.Run(); err != nil {
fmt.Println("Error: ", err)
}
Or, with Output() as in "Exec a shell command in Go":
cmd := exec.Command("job.sh")
out, err := cmd.Output()
if err != nil {
println(err.Error())
return
}
fmt.Println(string(out))
First of all do not forget to make your python script executable (permissions and #!/usr/local/bin/python at the beginning).
After this you can just run something similar to this (notice that it will report you errors and standard output).
package main
import (
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("script.py")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Println(cmd.Run())
}
Below worked for me on Windows 10
python := path.Clean(strings.Join([]string{os.Getenv("userprofile"), "Anaconda3", "python.exe"}, "/"))
script := "my_script.py"
cmd := exec.Command("cmd", python, script)
out, err := cmd.Output()
fmt.Println(string(out))
if err != nil {
log.Fatal(err)
}
How can I call an external command in GO?
I need to call an external program and wait for it to finish execution. before the next statement is executed.
You need to use the exec package : start a command using Command and use Run to wait for completion.
cmd := exec.Command("yourcommand", "some", "args")
if err := cmd.Run(); err != nil {
fmt.Println("Error: ", err)
}
If you just want to read the result, you may use Output instead of Run.
package main
import (
"fmt"
"os/exec"
"log"
)
func main() {
cmd := exec.Command("ls", "-ltr")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
}
Try online
Isn't this Golang program supposed to output a directory listing to stdout?
It compiles ok, but does nothing.
package main
import "exec"
func main() {
argv := []string{"-la"}
envv := []string{}
exec.Run("ls", argv, envv, "", exec.DevNull, exec.PassThrough, exec.MergeWithStdout)
}
this works:
package main
import "exec"
func main() {
cmd, err := exec.Run("/bin/ls", []string{"/bin/ls", "-la"}, []string{}, "", exec.DevNull, exec.PassThrough, exec.PassThrough)
if (err != nil) {
return
}
cmd.Close()
}
You could also do it in native go using: ioutil.ReadDir(dir), like so:
//listdir.go
package main
import (
"os"
"io/ioutil"
"fmt"
)
func ListDir(dir string) ([]os.FileInfo, error) {
return ioutil.ReadDir(dir)
}
func main() {
dir := "./"
if len(os.Args) > 1 {
dir = os.Args[1]
}
fi, err := ListDir(dir)
if err != nil {
fmt.Println("Error", err)
}
for _, f := range fi {
d := "-"
if f.IsDir() { d = "d" }
fmt.Printf("%s %o %d %s %s\n", d, f.Mode() & 0777, f.Size(), f.ModTime().Format("Jan 2 15:04"), f.Name())
}
}
Checkout the documentation available for ioutil and os packages.
By default exec.Command will leave standard input, output and error connected to /dev/null. So, your 'ls' command is running fine but the output is just being thrown away. If you add:
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
before the exec.Run call then your output will go where you probably expect it.
exec.Run replaces your program with the one it executes -- it never returns to your app. This means that when 'cd' completes, it will exit as normal, and the only effect should be of changing the directory; 'ls' will never run.