Echo text to a file in golang

huangapple go评论83阅读模式
英文:

Echo text to a file in golang

问题

在Golang中,可以使用类似下面的bash代码的方式进行操作:

package main

import (
	"fmt"
	"io/ioutil"
	"os"
)

func GrafanaProvData() {
	data := `apiVersion: 1

deleteDatasources:
  - name: Clickhouse
    orgId: 1

datasources:


  - name: Clickhouse
    type: vertamedia-clickhouse-datasource
    access: proxy
    url: http://localhost:8123
    basicAuth: 1
    basicAuthUser: default
    basicAuthPassword: $password
    isDefault: false
    defaultDatabase:`

	err := ioutil.WriteFile("/etc/grafana/provisioning/datasources/datasource.yml", []byte(data), os.ModePerm)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("File written successfully")
}

func main() {
	GrafanaProvData()
}

这段代码将数据写入到/etc/grafana/provisioning/datasources/datasource.yml文件中。你可以根据需要修改数据内容。

英文:

Is it possible to do something in Golang similar to the below in bash

GrafanaProvData()
{
cat > /etc/grafana/provisioning/datasources/datasource.yml << EOF
apiVersion: 1

deleteDatasources:
  - name: Clickhouse
    orgId: 1

datasources:


  - name: Clickhouse
    type: vertamedia-clickhouse-datasource
    access: proxy
    url: http://localhost:8123
    basicAuth: 1
    basicAuthUser: default
    basicAuthPassword: $password
    isDefault: false
    defaultDatabase:


EOF
}

i have tried command.exec but its not exactly what is needed

答案1

得分: 3

这个shell脚本的作用是使用所谓的“here document”(在链接中有详细介绍)来将shell的标准输入管道传递给命令cat,而cat命令的标准输出则被重定向到一个文件中。通过使用>进行重定向,文件要么被创建,要么被截断。

正如一些人评论的那样,在Go语言中,这基本上等同于将一个字符串字面量写入文本文件。

但请注意,有一个细节:当shell处理here-document时,它会在实际使用之前对文档的文本进行各种扩展,包括变量扩展,除非采取特殊的措施来阻止扩展。因此,在你的示例中,变量“password”的内容将替换$password的位置。

考虑到这一点,你可以这样做:

const tmpl = `apiVersion: 1

deleteDatasources:
  - name: Clickhouse
    orgId: 1

datasources:


  - name: Clickhouse
    type: vertamedia-clickhouse-datasource
    access: proxy
    url: http://localhost:8123
    basicAuth: 1
    basicAuthUser: default
    basicAuthPassword: %s
    isDefault: false
    defaultDatabase:
`

fd, err := os.OpenFile("/etc/grafana/provisioning/datasources/datasource.yml",
os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
  log.Fatal(err)
}
defer fd.Close()

_, err := fmt.Fprintf(tmpl, password)
if err != nil {
  log.Fatal(err)
}

另外,还有两点需要考虑:

  • 如果模板变得更加复杂,使用fmt.*f系列函数来完成任务会变得越来越困难;这时你可能需要查看text/template标准包。
  • 如果你需要对YAML模板进行更高级的处理,可能更方便的做法是停止使用格式无关的方法,而是使用Go中的任何YAML处理包,比如古老的gopkg.in/yaml.v3,首先填充一个自定义复合类型的变量,然后将其编组为一个YAML格式的文档。这样可以避免某些奇怪的错误(考虑一下:如果你的密码包含会被YAML解析器解释为合法YAML语法的字符,会发生什么?一个“真正”的YAML编组器会转义任何可能被错误解释的内容,而任何“模板化”方法则不会)。

还有一个提示:当要写入的文件很大(比如超过10k)时,将bufio.Writer放在fmt.Fprint*函数和打开的文件之间可能是值得的,以优化文件系统访问。

英文:

What happens in this shell script is the usage of the so-called "here document" which is piped by the shell to the standard input of the command cat whose standard output is directed to a file, which — due to the redirection set up using > — will be either created or truncated.

As commented by some folks, in Go, that would mostly amount to writing a string literal to a text file.

But note that there is a twist: when here-documents are processed by the shell it performs various expansions — including variable expansion — in the document's text before actually using it — unless special action is taken to prevent that.
So, in your example, the contents of the variable "password" would be substituted in place of $password.

With this in mind, what you'd do is something like

const tmpl = `apiVersion: 1

deleteDatasources:
  - name: Clickhouse
    orgId: 1

datasources:


  - name: Clickhouse
    type: vertamedia-clickhouse-datasource
    access: proxy
    url: http://localhost:8123
    basicAuth: 1
    basicAuthUser: default
    basicAuthPassword: %s
    isDefault: false
    defaultDatabase:
`

fd, err := os.OpenFile("/etc/grafana/provisioning/datasources/datasource.yml",
os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
  log.Fatal(err)
}
defer fd.Close()

_, err := fmt.Fprintf(tmpl, password)
if err != nil {
  log.Fatal(err)
}

While we are at it, consider two more things:

  • If/when the template becomes more complicated, it would become progressively harder to use the functions of the fmt.*f family to cut the task; you then might want to look at the text/template standard package.
  • If/when you would need even more advanced processing of your YAML templates, it might be more convenient to stop being format-agnostic and use any YAML-handling package for Go — such as the venerable gopkg.in/yaml.v3 to first fill a variable of some custom compound type and then marshal it as a YAML-formatted document.
    This might help prevent a certain class of weird erorrs (consider: what happens if your password contains letters which will be interpreted by a YAML parser as a proper YAML syntax? A "real" YAML marshaler would escape anything which would be improperly interpreted — while any "templating" approach would not.)

One more hint: when the file to be written is big (like, over 10k), it might be worthwhile to stick a bufio.Writer between the fmt.Fprint* functions and the opened file—to optimize filesystem access.

huangapple
  • 本文由 发表于 2021年11月22日 18:38:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/70064438.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定