I am running a command through the os/exec package that is called like this:
out, err := Exec("ffprobe -i '/media/Name of File.mp3' -show_entries format=duration -v quiet -of csv=p=0", true, true)
The function I have written to execute command line calls is:
func Exec(command string, showOutput bool, returnOutput bool) (string, error) {
log.Println("Running command: " + command)
lastQuote := rune(0)
f := func(c rune) bool {
switch {
case c == lastQuote:
lastQuote = rune(0)
return false
case lastQuote != rune(0):
return false
case unicode.In(c, unicode.Quotation_Mark):
lastQuote = c
return false
default:
return unicode.IsSpace(c)
}
}
parts := strings.FieldsFunc(command, f)
//parts = ["ffprobe", "-i", "'/media/Name of File.mp3'", "-show_entries", "format=duration", "-v", "quiet", "-of", "csv=p=0"]
if returnOutput {
data, err := exec.Command(parts[0], parts[1:]...).Output()
if err != nil {
return "", err
}
return string(data), nil
} else {
cmd := exec.Command(parts[0], parts[1:]...)
if showOutput {
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
}
err := cmd.Run()
if err != nil {
return "", err
}
}
return "", nil
}
The strings.Fields command splits the command on spaces and that is used as the string array to pass to the exec.Command function. The problem is that it is splitting the filename into different parts because of the space when that filepath needs to stay together. Even if I format the string array correctly so the filepath is in one part, the exec.Command still fails because there is a space. I need to be able to execute this script to honor the filepath as one argument with spaces.
You may use strings.Split(s, ":") on special character like : and switch " with back-tick,
Like this working sample (The Go Playground):
package main
import (
"fmt"
"strings"
)
func main() {
command := `ffprobe : -i "/media/Name of File.mp3" : -show_entries format=duration : -v quiet : -of csv=p=0`
parts := strings.Split(command, ":")
for i := 0; i < len(parts); i++ {
fmt.Println(strings.Trim(parts[i], " "))
}
}
output:
ffprobe
-i "/media/Name of File.mp3"
-show_entries format=duration
-v quiet
-of csv=p=0
try print cmd.Args after cmd := exec.Command("ffprobe", s...) (remove .Output() ):
for _, v := range cmd.Args {
fmt.Println(v)
}
something like this, to find out what happens to your args:
s := []string{"-i '/media/Name of File.mp3'", "-show_entries format=duration", "-v quiet", "-of csv=p=0"}
cmd := exec.Command("ffprobe", s...)
for _, v := range cmd.Args {
fmt.Println(v)
}
cmd.Args = []string{"ffprobe", "-i '/media/Name of File.mp3'", "-show_entries format=duration", "-v quiet", "-of csv=p=0"}
fmt.Println()
for _, v := range cmd.Args {
fmt.Println(v)
}
see:
// Command returns the Cmd struct to execute the named program with
// the given arguments.
//
// It sets only the Path and Args in the returned structure.
//
// If name contains no path separators, Command uses LookPath to
// resolve the path to a complete name if possible. Otherwise it uses
// name directly.
//
// The returned Cmd's Args field is constructed from the command name
// followed by the elements of arg, so arg should not include the
// command name itself. For example, Command("echo", "hello")
func Command(name string, arg ...string) *Cmd {
cmd := &Cmd{
Path: name,
Args: append([]string{name}, arg...),
}
if filepath.Base(name) == name {
if lp, err := LookPath(name); err != nil {
cmd.lookPathErr = err
} else {
cmd.Path = lp
}
}
return cmd
}
Edit 3- Try this
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command(`ffprobe`, `-i "/media/Name of File.mp3"`, `-show_entries format=duration`, `-v quiet`, `-of csv=p=0`)
for _, v := range cmd.Args {
fmt.Println(v)
}
fmt.Println(cmd.Run())
}
Well, I figured it out.
var parts []string
preParts := strings.FieldsFunc(command, f)
for i := range preParts {
part := preParts[i]
parts = append(parts, strings.Replace(part, "'", "", -1))
}
I needed to remove the single quotes from the arg passed into the exec.Command function.
Related
I am new in GO and mac,
and I'm trying to use sysctl for finding the full path name of running processes.
kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
if err != nil {
fmt.Println("error: ", err)
}
for _, proc := range kprocs {
name := string(proc.Proc.P_comm[:])
pid := proc.Proc.P_pid
extName, err := unix.SysctlKinfoProc("kern.proc.pathname", int(pid))
if err != nil {
fmt.Println("error: ", err)
}
and getting error: no such file or directory
am I using this function correcly?
EDIT
If I run the process like this: ./processName ,then I am not getting it's full path, for example /Users/username/go/src/processName - which is what I need.
All the solutions with ps will give the relative path, and I need someting that gives the absolute path of the process.
Here you go:
func printPath(pid int32) {
cmd := exec.Command("ps", append([]string{"xuwww", "-p", strconv.Itoa(int(pid))})...)
output, e := cmd.CombinedOutput()
if e != nil {
fmt.Println(e)
}
lines := strings.Split(string(output), "\n")
comps := strings.Fields(lines[1])
fmt.Println(comps[len(comps)-1])
}
func main() {
kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
if err != nil {
fmt.Println("error: ", err)
}
for _, proc := range kprocs {
pid := proc.Proc.P_pid
printPath(pid)
}
}
I didn't use unix.SysctlKinfoProc(), instead ran a shell command ps xuwww -p PID using exec.command(). It returns output as string. Parse the output string to get absolute path.
Output:
649 /System/Library/PrivateFrameworks/IMDPersistence.framework/XPCServices/IMDPersistenceAgent.xpc/Contents/MacOS/IMDPersistenceAgent 646 /System/Library/PrivateFrameworks/AuthKit.framework/Versions/A/Support/akd 643 /System/Library/CoreServices/pbs
Following my comment on your question you can get the information you are looking for by using lsof.
Similar to the other code example you can iterate over all known processes but for some processes it takes some time for the command to return.
package main
import (
"fmt"
"os/exec"
"golang.org/x/sys/unix"
)
func printPath(pid int32) {
cmd := exec.Command("bash", "-c", fmt.Sprintf("lsof -p %d -Fn | awk 'NR==5{print}' | sed \"s/n\\//\\//\"", pid))
output, e := cmd.Output()
if e != nil {
fmt.Println(e)
return
}
fmt.Printf("%s", output)
}
func main() {
kprocs, err := unix.SysctlKinfoProcSlice("kern.proc.all")
if err != nil {
fmt.Println("error: ", err)
}
for _, proc := range kprocs {
pid := proc.Proc.P_pid
fmt.Printf("getting for pid %d: ", pid)
printPath(pid)
}
}
The code output can be improved. Processes that have exited already now make the output kind of a mess :-/
I'm trying to find the path of the helloworld.java file so that I can pass it down to a compiler function.
What I have:
I'm expecting this to return the path, of type []byte and then stringify it, of the only helloworld.java file in this directory and then pass it down to Java() function.
filePath, _ := exec.Command("find", "./helloworld/workspace", "-name", "*.java").Output()
Java(string(filePath))
The Problem is that cmd := exec.Command("javac", filePath) in my java() function is not recognizing the file path therefore not compiling it.
But if I hardcode the path that I get from exec.Command("find) like this:
This works fine
cmd := exec.Command("javac", "./helloworld/workspace/src/main/java/com/coveros/demo/helloworld/HelloWorld.java")
err := cmd.Run()
But this does not work:
What am I missing, How do I fix this?
func Java(filePath string) {
fmt.Println("compiler start")
cmd := exec.Command("javac", filePath)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Println("compiler End")
}
I think the result from find is returning multiple possible paths which are separated by a newline "\n". The newline will be "hidden" if it is printed to the command line. You can try this fmt.Println(stringPath + "hello, am I on a new line?") to show the stringPath has a new line in it.
See the below which uses a similar version of find looking for json files, then splits the string by newlines and then loops through these paths. If the path is blank (which it can be) it skips over it.
package main
import (
"fmt"
"os/exec"
"strings"
)
func main() {
filePath, err := exec.Command("find", ".", "-name", "*.json").Output()
if err != nil {
panic(err)
}
stringPath := string(filePath)
paths := strings.Split(stringPath, "\n")
CatFile(paths)
}
func CatFile(filePaths []string) {
for _, path := range filePaths {
if len(path) == 0 {
continue
}
output, err := exec.Command("cat", path).Output()
if err != nil {
fmt.Println("Error!")
fmt.Println(err)
}
fmt.Println(string(output))
}
}
See this related question, which discusses this: Go lang differentiate "\n" and line break
The Go documentation (http://golang.org/pkg/flag/) says:
The FlagSet type allows one to define independent sets of flags, such as to implement subcommands in a command-line interface.
I need this functionality but I can't figure out how to persuade the flag pkg to do it. When I define two FlagSets, parsing one of them will give me errors and warnings if the commandline has flags that are meant for the second one. Example:
f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
apply := f1.Bool("apply", false, "")
silent := f1.Bool("silent", false, "")
if err := f1.Parse(os.Args[1:]); err == nil {
fmt.Println(*apply, *silent)
}
f2 := flag.NewFlagSet("f2", flag.ContinueOnError)
reset := f2.Bool("reset", false, "")
if err := f2.Parse(os.Args[1:]); err == nil {
fmt.Println(*reset)
}
I get all sorts of warnings if I try to do cmd -apply OR cmd -reset. I want to keep these FlagSets separate because I want to only have -silent work for -apply.
What am I missing?
You are meant to distinguish between subcommands first, and then call Parse on the right FlagSet.
f1 := flag.NewFlagSet("f1", flag.ContinueOnError)
silent := f1.Bool("silent", false, "")
f2 := flag.NewFlagSet("f2", flag.ContinueOnError)
loud := f2.Bool("loud", false, "")
switch os.Args[1] {
case "apply":
if err := f1.Parse(os.Args[2:]); err == nil {
fmt.Println("apply", *silent)
}
case "reset":
if err := f2.Parse(os.Args[2:]); err == nil {
fmt.Println("reset", *loud)
}
}
http://play.golang.org/p/eaEEx_EReX
Turns out it is possible to consume one set of flags while capturing the flags not recognized by the first set and while discarding the error messages that the flag package is prepared to emit whenever it runs into an option it doesn't recognize.
Here's one way to do it.
import (
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
)
func gentleParse(flagset *flag.FlagSet, args []string) []string {
if len(args) == 0 {
return nil
}
r := make([]string, 0, len(args))
flagset.Init(flagset.Name(), flag.ContinueOnError)
w := flagset.Output()
flagset.SetOutput(ioutil.Discard)
defer flagset.SetOutput(w)
next := args
for len(next) > 0 {
if next[0] == "--" {
r = append(r, next...)
break
}
if !strings.HasPrefix(next[0], "-") {
r, next = append(r, next[0]), next[1:]
continue
}
if err := flagset.Parse(next); err != nil {
const prefix = "flag provided but not defined: "
if strings.HasPrefix(err.Error(), prefix) {
pull := strings.TrimPrefix(err.Error(), prefix)
for next[0] != pull {
next = next[1:]
}
r, next = append(r, next[0]), next[1:]
continue
}
fmt.Fprintf(w, "%s\n", err)
flagset.SetOutput(w)
flag.Usage()
os.Exit(1)
}
next = flag.Args()
}
return r
}
Just change these code
if err := f2.Parse(os.Args[1:]); err == nil {
fmt.Println(*reset)
}
to
f2.Parse(os.Args[1:])
fmt.Println(*reset)
but the warning is just left on the console.if u wanna remove it ,modify /usr/local/go/src/flag/flag.go and recompile the golang ..
or do a copy of flag package.
→_→ 怀疑的眼神~~
I'm writing a small program with an interpreter, I would like to pipe any command that is not recognized by my shell to bash, and print the output as if written in a normal terminal.
func RunExtern(c *shell.Cmd) (string, os.Error) {
cmd := exec.Command(c.Cmd(), c.Args()...)
out, err := cmd.Output()
return string(out), err
}
this is what I've written so far, but it only executes a program with its args, I would like to send the whole line to bash and get the output, any idea how to do so ?
For example, to list directory entries in columns,
package main
import (
"exec"
"fmt"
"os"
)
func BashExec(argv []string) (string, os.Error) {
cmdarg := ""
for _, arg := range argv {
cmdarg += `"` + arg + `" `
}
cmd := exec.Command("bash", "-c", cmdarg)
out, err := cmd.Output()
return string(out), err
}
func main() {
out, err := BashExec([]string{`ls`, `-C`})
if err != nil {
fmt.Println(err)
}
fmt.Println(out)
}
Let's say I want to run 'ls' in a go program, and store the results in a string. There seems to be a few commands to fork processes in the exec and os packages, but they require file arguments for stdout, etc. Is there a way to get the output as a string?
There is an easier way now:
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)
}
Where out is the standard output. It's in the format []byte, but you can change it to string easily with:
string(out)
You can also use CombinedOutput() instead of Output() which returns standard output and standard error.
exec.Command
To get both stdout and stderr into separate strings, you can use byte buffers like so:
cmd := exec.Command("date")
var outb, errb bytes.Buffer
cmd.Stdout = &outb
cmd.Stderr = &errb
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Println("out:", outb.String(), "err:", errb.String())
cmd := exec.Command("ls", "-al")
output, _ := cmd.CombinedOutput()
fmt.Println(string(output))
or
cmd := exec.Command(name, arg...)
stdout, err := cmd.StdoutPipe()
cmd.Stderr = cmd.Stdout
if err != nil {
return err
}
if err = cmd.Start(); err != nil {
return err
}
for {
tmp := make([]byte, 1024)
_, err := stdout.Read(tmp)
fmt.Print(string(tmp))
if err != nil {
break
}
}
I used this with a recent version of GO (~1.11)
// CmdExec Execute a command
func CmdExec(args ...string) (string, error) {
baseCmd := args[0]
cmdArgs := args[1:]
log.Debugf("Exec: %v", args)
cmd := exec.Command(baseCmd, cmdArgs...)
out, err := cmd.Output()
if err != nil {
return "", err
}
return string(out), nil
}
// Usage:
// out, err := CmdExec("ls", "/home")
Two options, depending on the paradigm you prefer:
os.ForkExec()
exec.Run()
Use exec.Run, passing Pipe for stdout. Read from the pipe that it returns.
If you are wanting string output, strings.Builder is more efficient [1] than
bytes.Buffer:
package main
import (
"os/exec"
"strings"
)
func main() {
c, b := exec.Command("go", "version"), new(strings.Builder)
c.Stdout = b
c.Run()
print(b.String())
}
https://golang.org/pkg/bytes#Buffer.String
Edit: This answer is obsolete. Please see Fatih Arslan's answer below.
Use exec.Run by specifying Pipe as the stdout (and stderr if you want). It will return cmd, which contains an os.File in the Stdout (and Stderr) fields. Then you can read it using for example ioutil.ReadAll.
Example:
package main
import (
"exec";
"io/ioutil";
)
func main() {
if cmd, e := exec.Run("/bin/ls", nil, nil, exec.DevNull, exec.Pipe, exec.MergeWithStdout); e == nil {
b, _ := ioutil.ReadAll(cmd.Stdout)
println("output: " + string(b))
}
}