The main problem I see is a misunderstanding of how text.sh is executed. The first line, #!… tells the shell what command and arguments to run. That process is started and the contents of the file are ran by that process (see Shell_script for more information). For this case, we can implement that process by first getting the first line of text.sh and getting the command and its arguments (here /usr/bin/env and bash). These are used to create the command.
I then set the commands standard input to the contents of the file. They are converted to a bytes.Buffer because Stdin is an io.Reader.
Here is my working code:
package main
import (
"bytes"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
)
func main() {
log.SetFlags(log.Lshortfile)
// 1. save code from an external file in a variable
contents, err := ioutil.ReadFile("text.sh")
if err != nil {
log.Fatalln(err)
}
// 2. run the code in a terminal
// get the first line out, assume it starts with "#!"
firstLine := contents[:bytes.Index(contents, []byte{'\n'})]
if string(firstLine[:2]) != "#!" {
log.Fatalln("not a shell script")
}
args := strings.Split(string(firstLine[2:]), " ")
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = bytes.NewBuffer(contents)
out, err := cmd.Output()
if err != nil {
log.Fatalln(err)
}
os.Stdout.Write(out)
}
Some notes:
I shortened your file read code to the convenient ioutil.ReadFile. Were I not constrained by needing to run the code from a variable, I would have set cmd.Stdin to the file.
I think shells do something other than setting the command’s stdin because shell scripts can respond to user input. Depending on what you are trying to do, this difference may not matter.