Read file as template, execute it and write it back

27,254

Solution 1

Use ParseFiles to parse the template. This code basically does the same thing as calling ReadFile, template.New and Parse as in the question, but it's shorter.

t, err := template.ParseFiles(path)
if err != nil {
    log.Print(err)
    return
}

Use os.Create to open the output file.

f, err := os.Create(path)
if err != nil {
    log.Println("create file: ", err)
    return
}

A file is an io.Writer. You can execute the template directly to the open file:

err = t.Execute(f, config)
if err != nil {
    log.Print("execute: ", err)
    return
}

Close the file when done.

f.Close()

Complete working example on the playground.

Solution 2

Here is a function I made with Cerise Limón's answer

func createFileUsingTemplate(t *template.Template, filename string, data interface{}) error {
    f, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer f.Close()

    err = t.Execute(f, data)
    if err != nil {
        return err
    }

    return nil
}
Share:
27,254

Related videos on Youtube

user3147268
Author by

user3147268

Updated on September 15, 2021

Comments

  • user3147268
    user3147268 over 2 years

    I'm trying to parse CSS files in which variables can be injected that are defined in a config file. Currently the function does:

    1. Opens the file based on the given path argument
    2. Parses the file's content
    3. Executes the template by injecting the config variable
    4. Writes the rendered content to the console instead of the original file
    func parse(path string) {
        f, err := ioutil.ReadFile(path)
    
        if err != nil {
            log.Print(err)
            return
        }
    
        // Parse requires a string
        t, err := template.New("css").Parse(string(f))
    
        if err != nil {
            log.Print(err)
            return
        }
    
        // A sample config
        config := map[string]string {
            "textColor": "#abcdef",
            "linkColorHover": "#ffaacc",
        }   
    
        // Execute needs some sort of io.Writer
        err = t.Execute(os.Stdout, config)  
    
        if err != nil {
            log.Print("Can't execute ", path)
        }
    }
    

    My problem is that template.Parse() requires the content as string and template.Execute() an io.Writer as argument. I tried to open the file with os.Open() which returns a file object that implements the io.Writer interface. But how can I get the file's content as a string from such a file object in order to use it with Parse()?

    • user3147268
      user3147268 over 8 years
      I try to extend a static side generator. Therefore I need to do this only once.
  • user3147268
    user3147268 over 8 years
    I use os.Walk("root", filterFunc) and a filter to find all css files. if the script found one it calls parse() with the path. So, does it makes sense to call ParseFiles() if I need only to parse a single file? And lastly: os.Create(path) will overwrite the original file, right?
  • Cerise Limón
    Cerise Limón over 8 years
    For this scenario, you should call ParseFiles with a single file at a time. Caling ParseFiles with a single file saves code as noted in the answer. Yes, this overwrites the original file. If that's not what you are asking in the question, then please clarify.
  • user3147268
    user3147268 over 8 years
    No, that's exactly what I needed. Thank you very much.
  • Admin
    Admin about 6 years
    Great, thanks @CeriseLimón I needed this too. Could you please explain what this is? Never seen this before map[string]string
  • Cerise Limón
    Cerise Limón about 6 years
    @Sbe88 It's a map with string keys and string values.