Try to run code from an external file in a terminal

Hello everyone

I would like to save a code from an external file (.sh) in a variable and then run the code in a terminal:

The external file contains (text.sh):

#!/usr/bin/env bash
echo "successful"

My previous code ist the following:

package main

import ("fmt"; "os"; "os/exec")

func main() {

    out, _ := exec.Command(iw()).Output()
    fmt.Println(out)

}

func iw() string {
    file, err := os.Open("text.sh")
    if err != nil {
        return "Error"
    }
    defer file.Close()

    stat, err := file.Stat()
    if err != nil {
        return "Error"
    }
    
    bs := make ([]byte, stat.Size())
    _, err = file.Read(bs)
    if err != nil {
        return "Error"
    }

    str := string(bs)
    return str
}

But if i run the go file in a terminal I only see:

[]

Have someone an idea where the problem is?

Check the error returned by exec.Command().Output() …

Thanks for providing code :smile:

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:

  1. 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.
  2. 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.
  3. Always check your errors. https://pocketgophers.com/error-checking-while-prototyping/ shows a simple way to do this while working with example or prototype code.

Hope this helps.

It works, thank you so much :smiley:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.