How to directly invoke the system shell in Go (golang)? - bash

As per the golang documentation, go does not make a call to the system's shell when you are using exec.Command().
From the golang.org documentation on the "os/exec" package:
Unlike the "system" library call from C and other languages, the os/exec package intentionally does not invoke the system shell and does not expand any glob patterns or handle other expansions, pipelines, or redirections typically done by shells.
This presents a problem. Because of this design choice you cannot use piping when executing a command. Therefore the following code does not execute as desired.
package main
import (
"fmt"
"os/exec"
)
func main() {
exec.Command("echo", "Hello", ">>", "~/thing").Run()
cmdOut, _ := exec.Command("cat", "~/thing").Output()
fmt.Println(cmdOut)
}
Instead of printing out the contents of a file that should contain the word 'Hello,' it instead prints out a blank newline. I have tried directly invoking bash like this:
package main
import (
"fmt"
"os/exec"
)
func main() {
exec.Command("bash", "-c", "echo", "Hello", ">>", "~/thing").Run()
cmdOut, _ := exec.Command("cat", "~/thing").Output()
fmt.Println(cmdOut)
}
This, however, produces the same result as the original code. How can I directly invoke the system shell when using golang?

The second argument should be one string. In shell command you need to pass it as one string too. Also ~ is interpreted by bash. You can safely assume that sh exists. Bash shell is not a must.
package main
import (
"fmt"
"os/exec"
)
func main() {
exec.Command("sh", "-c", "echo Hello >> ~/thing").Run()
cmdOut, _ := exec.Command("sh", "-c", "cat ~/thing").Output()
fmt.Println(cmdOut)
}

Related

How to capture the output of the terminal using Go?

I want to create a simple program using Go that can get an output in the terminal output. For example:
echo "john" | goprogram
The output is hi john
When using command cat
cat list_name.txt | goprogram
The output using
hi doe
hi james
hi chris
Is there a way to do this using Go?
Read from os.Stdin. Here's an example implementation of the Hi program.
package main
import (
"bufio"
"fmt"
"log"
"os"
)
func main() {
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
fmt.Println("hi", s.Text())
}
if s.Err() != nil {
log.Fatal(s.Err())
}
}
This program creates a scanner to read os.Stdin by line. For each line in stdin, the program prints "hi" and the line.

infinite loop when bash call Go program input

I wrote a bash script to automate download my Go program and install to my linux machine. I can via the curl to download but when my Go program prompt the user input, it go to infinite loop. The error shows EOF error.
Do anyone have any idea about it?
Install.sh
#!/bin/bash
set -e
set -o noglob
curl https://coderkk.net/ReadInput -o ReadInput
chmod a+x ReadInput
./ReadInput
ReadInput.go
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
var text string
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("Please enter you name here : ")
text, _ = reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
if text != "" {
break
}
}
fmt.Printf("Hi %s\n", text)
}
The problem is not related to Go - although you shouldn't ignore any potential errors from reader.ReadString - it is due to how you are invoking your downloaded script.
The pipe takes the output of curl and turns that into the stdin for sh. This is fine but your script requires stdin from the terminal - which is now lost.
Change your invocation method to something like this:
sh -c "$(curl -sfL https://coderkk.net/install.sh)"
this will pass the contents of your script to the sh interpreter but preserve the user's terminal stdin.

exec.Command with bash -c does not return stdout

Executing the following program, out is an empty slice of type []uint8.
package main
import (
"context"
"log"
"os/exec"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "bash", "-c", "python3", "-c", "print('hello')")
out, _ := cmd.Output()
log.Println(out)
}
However, if I run without bash -c, I get the expected output.
This is a trivial example where bash -c isn't needed, but in the real world, my app is invoking a python script which imports several packages, and without bash -c, I get "module not found" errors from python.
What am I doing wrong here? How can I capture the stdout contents?
bash -c should be followed by one string argument with the command to execute, then bash will do the argument processing.
cmd := exec.CommandContext(ctx, "bash", "-c", "python3 -c 'print(\"hello\")'")

golang - read string argument include &

How do I read string argument include & in Go for example this link
$ ./main
https://www.youtube.com/watch?v=G3PvTWRIhZA&list=PLQVvvaa0QuDeF3hP0wQoSxpkqgRcgxMqX
without use double quotation (")
$ ./main
"https://www.youtube.com/watch?v=G3PvTWRIhZA&list=PLQVvvaa0QuDeF3hP0wQoSxpkqgRcgxMqX"
main.go
package main
import (
"os"
"fmt"
)
func main() {
link := os.Args[1]
fmt.Println(link)
}
$ go build main.go
$ ./main
https://www.youtube.com/watch?v=G3PvTWRIhZA&list=PLQVvvaa0QuDeF3hP0wQoSxpkqgRcgxMqX
output will be
https://www.youtube.com/watch?v=G3PvTWRIhZA
Both #JimB and #Adrian are correct, the & needs to be escaped.
If you absolutely must find a workaround, you could opt to not use a command-line argument and rather read input instead to bypass need for escaping.
Example:
package main
import (
"fmt"
)
func main() {
var input string
fmt.Scan(&input)
fmt.Println(input)
}
input:
$ ./main
https://www.youtube.com/watch?v=G3PvTWRIhZA&list=PLQVvvaa0QuDeF3hP0wQoSxpkqgRcgxMqX
output:
https://www.youtube.com/watch?v=G3PvTWRIhZA&list=PLQVvvaa0QuDeF3hP0wQoSxpkqgRcgxMqX

Using Go to spawn a shell with a TTY from "nc -e /bin/bash"

I want to escape a restricted shell spawning a bash shell via Go. In other words, I want to do this but using Go:
python -c 'import pty; pty.spawn("/bin/bash")'
I am totally new to Go. I have tried this (following the answer in this question Go: How to spawn a bash shell) but nothing happens:
package main
import "os"
import "os/exec"
func main() {
shell := exec.Command("/bin/bash")
shell.Stdout = os.Stdout
shell.Stdin = os.Stdin
shell.Stderr = os.Stderr
shell.Run()
}
Also if I add the fmt.Println("hello") line at the end of the main function nothing is printed
UPDATE
Maybe I did not expalined well. What I am trying to achieve it's to spawn a shell gotten a restricted shell. This is what I did:
Listener:
nc.traditional -l -p 8080 -e /bin/bash
Connects to listener: And I exec the code here
nc.traditional localhost 8080 -v
Your program works fine for me. I put some error checking in and an extra print statement which should make what is happening clearer. You are getting an interactive shell, it just looks exactly like your previous shell.
$ go run Go/shell.go
$ # whoa another shell
$ exit
exit
exiting
$ # back again
$
Here is the revised program
package main
import (
"fmt"
"log"
"os"
"os/exec"
)
func main() {
shell := exec.Command("/bin/bash")
shell.Stdout = os.Stdout
shell.Stdin = os.Stdin
shell.Stderr = os.Stderr
err := shell.Run()
if err != nil {
log.Fatalf("command failed: %v", err)
}
fmt.Printf("exiting\n")
}

Resources