How to convert from *string (filename) to io.Writer?

Initially, my code was like:

t := template.Must(template.New("").Parse(someInfileTemplate))
err = t.Execute(os.Stdout, links)

I was able to render and print the template to the os.Stdout.

Now I have expanded the code and am taking a outFileFlag which is a String flag from argparse module which returns a pointer to string. argparse should not be a problem because stdlib flag module returns the same pointer to string.

I tried using ioutil.WriteFile but got confused.

How do I convert a pointer to a string to io.Writer which template.Execute takes?

What is the signature of this method? What is the type of the first parameter: os.File?

Its probably text.template.(*Template).Execute() or its html equivalent.

I have updated the question.

t is template.Must(template.New("").Parse(someInfileTemplate))

You can not make it take a filename directly.

You need to use os.OpenFile() to get an *os.File, which implements io.Writer.

1 Like
	f, err := os.Open(*outFileFlag)
	if err != nil {
		exit("Error opening the out file.")
	}

	t := template.Must(template.New("").Parse(someInfileTemplate))
	err = t.Execute(f, links)
	if err != nil {
		exit("Cannot execute the template")
	}

The program exits at first exit().

os.Open opens for reading, as I said you need os.OpenFile().

And perhaps you should actually fmt.Print() the err. Thats how you learn what went wrong.

f, err := os.OpenFile(*outFileFlag, os.O_CREATE, 644)
if err != nil {
	panic(err)
}

This would work on any other file, but won’t on os.Stdout:

panic: open os.Stdout: permission denied

I do not understand. How do you pass os.Stdout there?

No need to open Stdout. It is already ready to use as it is defined in os.go
var (
Stdin = NewFile(uintptr(syscall.Stdin), “/dev/stdin”)
Stdout = NewFile(uintptr(syscall.Stdout), “/dev/stdout”)
Stderr = NewFile(uintptr(syscall.Stderr), “/dev/stderr”)
)

You are right! I have decided to use some predefined filename now.

Why predefined? You can still use flags to get it from the user as a command line argument. But using os.Stdout doesn’t make sense in that scenario.

Especially not as a filename…

That is why I’m confused.

Yes, that is what I mean. Users can still override the flag.

The thing which is predefined is default file name. :relaxed:

Okay, I do understand now.

Have you considered following some linux convention and use - as the default filename, meaning os.Stdout?

func writerFromFlag(flag *string) (io.Writer, error) {
  if *flag == "-" {
    return os.Stdout, err
  }
  return os.OpenFile(*flag, os.O_CREATE, 644)
}