I have a program that reads a filename from the console and executes go run filename.go.
// main.go
package main
import (
"bufio"
"fmt"
"log"
"os"
"os/exec"
)
func main() {
console := bufio.NewReader(os.Stdin)
fmt.Print("Enter a filename: ")
input, err := console.ReadString('\n')
if err != nil {
log.Fatalln(err)
}
input = input[:len(input)-1]
gorun := exec.Command("go", "run", input)
result, err := gorun.Output()
if err != nil {
log.Println(err)
}
fmt.Println("---", input, "Result ---")
fmt.Println(string(result))
}
In the same directory, I have another file like this.
// hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
When I input "hello.go" in the console, that file is run, and its output gets returned to the parent Go process. However, I have another program like this.
// count.go
package main
import (
"fmt"
"time"
)
func main() {
i := 0
for {
time.Sleep(time.Second)
i++
fmt.Println(i)
}
}
Except, because this program never returns, my parent process is left hanging forever. Is there a way to communicate with different Go processes? I'm thinking something like channels for goroutines, but for processes. I need to be able to receive live stdout from the child process.
The problem I'm trying to solve is dynamically executing Go programs from a directory. Go files will be added, removed, and modified daily. I'm kind of trying to make something like Go Playgrounds. The main process is a webserver serving webpages, so I can't shut it down all the time to modify code.
Don't use go run, you need to do what go run is doing yourself to have the go program be a direct child of your server process.
Using go build -o path_to/binary source_file.go will give you more control. Then you can can directly execute and communicate with the resulting binary.
Related
I'm trying to use Go's concurrency to create a script that runs multiple nmap scans with different options but whenever I try to run the program it exits after the 1st nmap scan completes. Is there a way to prevent this?
here's the code:
package main
import (
"syscall"
"os"
"os/exec"
"fmt"
)
func main(){
args1 := []string{"nmap","-sS","127.0.0.1"}
args2 := []string{"nmap","-sN","127.0.0.1"}
args3 := []string{"nmap","-sV","127.0.0.1"}
go funccmd(args1)
go funccmd(args2)
go funccmd(args3)
fmt.Scanln()
}
func funccmd(args []string){
env := os.Environ()
cmdpath, runErr := exec.LookPath("/usr/bin/nmap")
if runErr != nil {
panic(runErr)
}
execErr := syscall.Exec(cmdpath, args, env)
if execErr != nil {
panic(execErr)
}
}
Do not use syscall.Exec. That calls execve which replaces the current program with nmap. Use exec.Cmd to execute nmap instead.
I need to read golang code from the play.golang.org link and save to a .go file. I'm wondering if there is any public API support for play.golang.org. I googled but no hints. Has anyone attempted anything similar?
To get the text of a shared playground program, append ".go" to the URL. For example, you can get the text of the program at https://play.golang.org/p/HmnNoBf0p1z with https://play.golang.org/p/HmnNoBf0p1z.go.
You can upload a program by posting the program text to https://play.golang.org/share. The response is the ID of the shared program. This program uploads stdin to the playground and prints the ID of the uploaded program to stdout:
package main
import (
"io"
"log"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("POST", "https://play.golang.org/share", os.Stdin)
if err != nil {
log.Fatal(err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
}
Assuming the above program is in upload.go, the following shell script prints HmnNoBf0p1z.
go run upload.go << EOF
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
}
EOF
If you want to download the program as a file using a browser, then add the query ?download=true to the .go URL. Example: https://play.golang.org/p/HmnNoBf0p1z.go?download=true
There are two ways that I know one of which is described by #ThunderCat. Another simple solution is go to the URL https://play.golang.org/p/HmnNoBf0p1z and Press Ctrl+save on the page it will be downloaded as a .go file.
lets say I have this code:
package main
import (
"io/ioutil"
"fmt"
)
func check(err error) {
if err != nil {
panic(err)
}
}
func main() {
file, err := ioutil.ReadFile("test.txt")
check(err)
fmt.Print(string(file))
}
when running it with go run I want it to be written in a cleared bash window. Is it possible to do so without using any additional open-source repositories?
Thanks in advance.
If clearing the terminal is truly part of your program's responsibility then check out the answers in this question How can I clear the terminal screen in Go?
However if you're just wanting to clear the screen as part of your development process then I would keep it simple and do something like this
clear && go run *.go
So this is the example from Programming in Go by Mark Summerfield.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var britishAmerican = "british-american.txt"
func init() {
dir, _ := filepath.Split(os.Args[0])
britishAmerican = filepath.Join(dir, britishAmerican)
}
func main() {
rawBytes, err := ioutil.ReadFile(britishAmerican)
if err != nil {
fmt.Println(err)
}
text := string(rawBytes)
usForBritish := make(map[string]string)
lines := strings.Split(text, "\n")
fmt.Println(lines)
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 2 {
usForBritish[fields[0]] = fields[1]
}
}
fmt.Println(usForBritish)
}
When I run this code with the init() func commented out, it works perfectly fine. If I leave it in I get this error:
open /var/folders/l6/rdqtyrfd303dw1cz8qvlfcvc0000gn/T/go- build652175567/command-line-arguments/_obj/exe/british-american.txt: no such file or directory exit status 1
My question is, why does the init() func not grab the file from the appropriate directory?
You change the variable britishAmerican in the init function. Without init(), the program looks in the current directory (no path given, only the file name). With init(), it looks in the path where the executable is (os.Args[0]). And with go run main.go, the directory with the executable is not the current working directory.
You should use go build to build the binary and then run it, or you should tell us what you want to achieve (as written by #RoninDev).
The MCVE I've mentioned could look like this:
package main
import (
"io/ioutil"
"log"
"os"
"path/filepath"
)
var filename = "foo.txt"
func init() {
// change to true and things break
if false {
dir, _ := filepath.Split(os.Args[0])
filename = filepath.Join(dir, filename)
}
}
func main() {
// requires a file 'foo.txt' in the current directory
_, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
}
It can (of course) be even shorter, but this should be enough for the others in the community to see what is going on.
It looks to me like the program is expecting a file called british-american.txt in the directory that the executable is in.
That is what the code in init() does - it finds the path the the executable and constructs a path to the dictionary relative to that.
I can see from your error message that you are using go run to run the code. This makes a temporary executable in /tmp and runs that. If you leave the init() code in then it will look for the dictionary in the /tmp directory and it won't find it. If you take the init() code out it will look for the dictionary in the current directory and it will succeed.
If you want to use it as the author intended then use go build to build a binary and then run it - that will work.
I am learning Go programming language. Please consider the following program,
package main
import (
"fmt"
"bytes"
"os"
"os/exec"
"path/filepath"
"sync"
)
func grep(file string) {
defer wg.Done()
cmd := exec.Command("grep", "-H", "--color=always", "add", file)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Run()
fmt.Printf("%s\n", out.String())
}
func walkFn(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
wg.Add(1)
go grep (path)
}
return nil
}
var wg sync.WaitGroup
func main() {
filepath.Walk("/tmp/", walkFn)
wg.Wait()
}
This program walks all the files in the /tmp directory, and does a grep on each file in a goroutine. So this will spawn n goroutines where n is the number of files present in the /tmp directory. Main waits till all goroutines finishes the work.
Interestingly, this program take same time to execute with and without goroutines. Try running go grep (path, c) and grep (path, c) (you need to comment channel stuff when doing this).
I was expecting goroutine version to run faster as multiple grep runs concurrently. But it executes almost in equal time. I am wondering why this happens?
Try using more cores. Also, use a better root directory for comparative purposes, like the Go directory. An SSD makes a big difference too. For example,
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
goroot := "/home/peter/go/"
filepath.Walk(goroot, walkFn)
wg.Wait()
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
}
GOMAXPROCS: 1
real 0m10.137s
user 0m2.628s
sys 0m6.472s
GOMAXPROCS: 4
real 0m3.284s
user 0m2.492s
sys 0m5.116s
Your program's performance is bound to the speed of the disk (or ram, if /tmp is a ram disk): the computation is I/O bound. No matter how many goroutines run in parallel it can't read faster than that.