I must missing something basic because the following is not working as expected.
go run test_fd.go <(cat) under either bash or zsh results in the following:
Expected output
$ go run test_fd.go <(cat) # where n is some fd number
fd {{n}} filename /dev/fd/{{n}}
Hello!
Actual output (Zsh)
$ go run test_fd.go <(cat)
fd 11 filename /dev/fd/11
panic: Write() err write /dev/fd/11: bad file descriptor
goroutine 1 [running]:
panic(0xefe80, 0xc820010200)
/usr/local/Cellar/go/1.6/libexec/src/runtime/panic.go:464 +0x3e6
main.main()
/Users/bmf/Projects/piper/main.go:32 +0x515
exit status 2
Actual output (Bash)
$ go run main.go <(cat)
fd 63 filename /dev/fd/63
panic: Write() err write /dev/fd/63: bad file descriptor
goroutine 1 [running]:
panic(0xefe80, 0xc82006c240)
/usr/local/Cellar/go/1.6/libexec/src/runtime/panic.go:464 +0x3e6
main.main()
/Users/bmf/Projects/piper/main.go:32 +0x515
exit status 2
Source
// test_fd.go
package main
import (
"fmt"
"os"
"regexp"
"strconv"
)
var fdRegex = regexp.MustCompile(`\A/dev/fd/(\d+)\z`)
func main() {
for _, filename := range os.Args {
fdStrMatch := fdRegex.FindStringSubmatch(filename)
if len(fdStrMatch) != 2 {
continue
}
fd, _ := strconv.Atoi(fdStrMatch[1]) // fdStrMatch[1] is \d+
fmt.Fprintf(os.Stderr, "fd %d filename %s\n", fd, filename)
f := os.NewFile(uintptr(fd), filename)
/*
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0777) // have tried many combinations of modes
if err != nil {
panic(fmt.Sprintf("Create() err %v", err))
}
*/
_, err := f.Write([]byte("Hello!\n"))
if err != nil {
panic(fmt.Sprintf("Write() err %v", err))
}
}
}
Wrong form of process substitution:
$ go run test_fd.go >(cat)
<(...) is read-only from the process via some file, usually /dev/fd/*
>(...) is write-only from the process via some file usually /dev/fd/*
Related
i want run multiple command in "os/exec" in one process, as you see below some command like "cd" not worked.
func main() {
cmd := exec.Command("ls")
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
fmt.Print(string(cmdOutput.Bytes()))
fmt.Println(".......... cd .........")
cdOutput := &bytes.Buffer{}
cdcomand:=exec.Command("cd","model")
cdcomand.Stdout = cdOutput
err = cdcomand.Run()
fmt.Print(string(cdOutput.Bytes()))
fmt.Println(".......... ls .........")
lsOutput := &bytes.Buffer{}
lscmd:=exec.Command("ls")
lscmd.Stdout = lsOutput
err = lscmd.Run()
if err != nil {
os.Stderr.WriteString(err.Error())
}
fmt.Print(string(lsOutput.Bytes()))}
i try with another way:
package main
import (
"os/exec"
"bytes"
"os"
"fmt"
)
func main() {
cmd := exec.Command("ls")
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
fmt.Print(string(cmdOutput.Bytes()))
fmt.Println(".......... cd and ls .........")
cdOutput := &bytes.Buffer{}
cdcomand:= exec.Command("sh", "-c", "ls && cd model")
cdcomand.Stdout = cdOutput
err = cdcomand.Run()
fmt.Print(string(cdOutput.Bytes()))
fmt.Println(".......... ls .........")
lsOutput := &bytes.Buffer{}
lscmd:=exec.Command("ls")
lscmd.Stdout = lsOutput
err = lscmd.Run()
if err != nil {
os.Stderr.WriteString(err.Error())
}
fmt.Print(string(lsOutput.Bytes()))
}
it did not work too.
in cmd document writes:
A Cmd cannot be reused after calling its Run, Output or CombinedOutput methods.
i've searched all tuts and docs for a way to do this, but i could not find any things. there was no solution in Executing external commands in Go article and advanced command execution in Go with os
each cmd command execute in different process so command like "cd" will not change directory . is there any way run multiple command "os/exec" in one proccess in golang?
Yes!
You could use sh -c "ls && cd model"
cmd := exec.Command("sh", "-c", "ls && cd model")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err = cmd.Run()
On ubuntu
$ man sh
DASH(1) BSD General Commands Manual DASH(1)
NAME
dash — command interpreter (shell)
-c Read commands from the command_string operand instead of from the standard input. Special parameter 0 will be set from the command_name
operand and the positional parameters ($1, $2, etc.) set from the remaining argument operands.
An example using:
$ go version
go version go1.10.2 linux/amd64
// cmd/test/main.go
package main
import (
"bytes"
"os/exec"
"fmt"
)
func main() {
var stdout, stderr bytes.Buffer
cmd := exec.Command("sh", "-c", "echo 'hello' && echo 'hi'")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
fmt.Println(err)
out := stdout.String() + stderr.String()
fmt.Printf(out)
}
$ go run cmd/test/main.go
<nil>
hello
hi
Situation:
I want to run a command that puts itself into the background. If it makes it more possible, then I'll run the command in foreground and bring it into the background by myself.
Question:
When the process runs in background: how can I get it's pid using Go?
I tried the following:
cmd := exec.Command("ssh", "-i", keyFile, "-o", "ExitOnForwardFailure yes", "-fqnNTL", fmt.Sprintf("%d:127.0.0.1:%d", port, port), fmt.Sprintf("%s#%s", serverUser, serverIP))
cmd.Start()
pid := cmd.Process.Pid
cmd.Wait()
This returns instantly and leaves ssh running in the background. But it's pid is not the pid of the running ssh process. Moreover, it's the pid of the parent ssh process before it forked and backgrounded itself.
You don't need anything special, just don't tell ssh to background itself and don't Wait() for it. Example:
$ cat script.sh
#!/bin/sh
sleep 1
echo "I'm the script with pid $$"
for i in 1 2 3; do
sleep 1
echo "Still running $$"
done
$ cat proc.go
package main
import (
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("./script.sh")
cmd.Stdout = os.Stdout
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
log.Printf("Just ran subprocess %d, exiting\n", cmd.Process.Pid)
}
$ go run proc.go
2016/09/15 17:01:03 Just ran subprocess 3794, exiting
$ I'm the script with pid 3794
Still running 3794
Still running 3794
Still running 3794
#Mostafa Hussein, can use goroutine waiting, manage process
function main()
cmd := exec.Command( "shell.sh" )
err := cmd.Start()
if err != nil {
return err
}
pid := cmd.Process.Pid
// use goroutine waiting, manage process
// this is important, otherwise the process becomes in S mode
go func() {
err = cmd.Wait()
fmt.Printf("Command finished with error: %v", err)
}()
return nil
}
I need to run a long-running child process and kill it if I quit (for any reason) out of parent application.
Here is the code:
cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
log.Fatal(err)
}
var fail io.ReadCloser
fail.Close()
The fail here produces obvious
panic: runtime error: invalid memory address or nil pointer dereference
It works as expected - the child process is killed.
But this happens in a goroutine:
cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
log.Fatal(err)
}
go func() {
var fail io.ReadCloser
fail.Close()
}()
The panic still happens, but then it seems defer is not called and the child process is not killed.
Any way to go around this?
UPDATE I need a cross-platform solution (at least for Linux and FreeBSD)
Minimal example:
infinite-loop.sh
#!/bin/bash
while true; do
sleep 1
done
Don't forget to
chmod +x infinite-loop.sh
test1.go (error checking left out for brevity):
package main
import (
"time"
"io"
"os/exec"
"runtime"
)
func main() {
cmd := exec.Command("./infinite-loop.sh")
cmd.Start()
defer cmd.Process.Kill()
go func() {
time.Sleep(100 * time.Millisecond)
var fail io.ReadCloser
fail.Close()
}()
for {
runtime.Gosched()
}
}
Let's run
ps aux | grep infinite-loop.sh | grep -v grep | wc -l; \
go run test1.go; \
ps aux | grep infinite-loop.sh | grep -v grep | wc -l
0 <--- !!
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x2130]
goroutine 5 [running]:
main.main.func1()
.../multiline/test1.go:19 +0x30
created by main.main
.../multiline/test1.go:20 +0x9a
goroutine 1 [runnable]:
runtime.Gosched()
/usr/local/Cellar/go/1.5.1/libexec/src/runtime/proc.go:166 +0x14
main.main()
.../multiline/test1.go:23 +0x9f
exit status 2
1 <--- !!
0 processes before and 1 after exit.
If you comment out goroutine code - it works fine.
Now we can kill it:
kill $(ps aux | grep infinite-loop.sh | grep -v grep | awk {'print $2'})
There's no cross-platform solution to automatically kill a child process.
On Linux, you can use the pdeathsig functionality:
cmd := exec.Command("./long-process")
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
On other platforms, the child needs to determine when to exit on its own. One way is to monitor a pipe or socket FD given to it from the parent. You could also have a process manager of some sort monitor the processes and cleanup if something goes wrong.
In general though, panics should be rare and get fixed. If you do have areas of code that are prone to panic'ing, you can recover locally and call for the cleanup of child processes before exiting.
For fun and to better learn Go, I'm trying to re-implement antigen in Go.
Problem is: source is a shell built-in function, so I can't call it with os/exec Command function, because it expects an executable in PATH.
How can I do this? And, is it possible to make a source from inside a go program affect the user shell?
You can write the command directly in the terminal device. But, to do that, first you need to know which device is using the user. A script that executes your program can be a solution.
#!/bin/bash
echo Running from foo script, pid = $$
go run foo.go `tty`
Then, the program has to write the commands to the terminal device.
package main
import (
"C"
"fmt"
"os"
"syscall"
"unsafe"
)
func main() {
// Get tty path
if len(os.Args) < 2 {
fmt.Printf("no tty path\n")
os.Exit(1)
}
ttyPath := os.Args[1]
// Open tty
tty, err := os.Open(ttyPath)
if err != nil {
fmt.Printf("error opening tty: %s\n", err.Error())
os.Exit(2)
}
defer tty.Close()
// Write a command
cmd := "echo Hello from go, pid = $$\n"
cmdstr := C.CString(cmd)
cmdaddr := uintptr(unsafe.Pointer(cmdstr))
for i := range []byte(cmd) {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, tty.Fd(), syscall.TIOCSTI, cmdaddr+uintptr(i))
if uintptr(err) != 0 {
fmt.Printf("syscall error: %s\n", err.Error())
os.Exit(3)
}
}
}
Here is an example output:
$ echo $$
70318
$ ./foo
Running from foo script, pid = 83035
echo Hello from go, pid = $$
$ echo Hello from go, pid = $$
Hello from go, pid = 70318
Note that I am executing the script with ./ not source, so the PID of the script differs. But later, the command executed by the go program has the same PID.
I've run into an issue with the os/exec library. I want to run a shell and pass it multiple commands to run, but it's failing when I do. Here's my test code:
package main
import (
"fmt"
"os/exec"
)
func main() {
fmt.Printf("-- Test 1 --\n`")
command1 := fmt.Sprintf("\"%s\"", "pwd") // this one succeeds
fmt.Printf("Running: %s\n", command1)
cmd1 := exec.Command("/bin/sh", "-c", command1)
output1,err1 := cmd1.CombinedOutput()
if err1 != nil {
fmt.Printf("error: %v\n", err1)
return
}
fmt.Printf(string(output1))
fmt.Printf("-- Test 2 --\n")
command2 := fmt.Sprintf("\"%s\"", "pwd && pwd") // this one fails
fmt.Printf("Running: %s\n", command2)
cmd2 := exec.Command("/bin/sh", "-c", command2)
output2,err2 := cmd2.CombinedOutput()
if err2 != nil {
fmt.Printf("error: %v\n", err2)
return
}
fmt.Printf(string(output2))
}
When running this I get an error 127 on the second example. It seems like it's looking for a literal "pwd && pwd" command instead of evaluating it as a script.
If I do the same thing from the command line it works just fine.
$ /bin/sh -c "pwd && pwd"
I'm using Go 1.4 on OS X 10.10.2.
the quotes are for your shell where you typed the command line, they should not be included when programatically launching an app
just make this change and it will work:
command2 := "pwd && pwd" // you don't want the extra quotes