Go实现替换(覆盖)文件某一行内容

1、前言

有这样一个需求,我们查找到文件中带有某个关键词的一行内容后,对该行内容进行替换,替换成我们需要的新内容,比如修改网络配置文件、修改图片地址、修改代码中所有关键词等,类似于编辑器中的关键词替换功能,只不过我们是直接判断文件而已。

2、实现覆盖某一行文件内容的思路

  • 1、打开文件
  • 2、读取文件每一行
  • 3、根据关键词判断是否是需要覆盖的行,是的话则从行开头写内容,使其覆盖该行旧内容

由于是覆盖,所以我们有一个前提是新写入的内容长度需要大于等于旧内容的长度,至于新内容小于旧内容的情况下,我们在扩展中再做尝试,基本思路包括两个:1、写入空内容覆盖多出的位置(应该不行,可以试一下);2、新内容写入后直接加入换行,然后将之前文件剩余内容覆盖多出来的这部分,还是覆盖的思想。

3、实现覆盖某一行内容的代码示例

我们这里以修改我的虚拟机中的网络配置文件为例做一个简单的示例(记得先备份):

原本的Ubuntu配置文件内容:

$ cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

auto ens33
iface ens33 inet static
address 40.40.40.210
gateway 40.40.40.1
netmask 255.255.255.0
复制代码

然后我们通过address、gateway、netmask等关键词来修改最后三行的内容,以此来修改配置文件中的ip地址、网关和子网掩码。

代码内容:

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
)

func main() {
    //读写方式打开文件
	file, err := os.OpenFile("/etc/network/interfaces", os.O_RDWR, 0666)
	if err != nil {
		fmt.Println("open file filed.", err)
		return
	}
    //defer关闭文件
	defer file.Close()

    //获取文件大小
	stat, err := file.Stat()
	if err != nil {
		panic(err)
	}
	var size = stat.Size()
	fmt.Println("file size:", size)

    //读取文件内容到io中
	reader := bufio.NewReader(file)
	pos := int64(0)
	ip := "40.40.40.220"
	gateway := "40.40.40.1"
	netmask := "255.255.255.0"
	for {
        //读取每一行内容
		line, err := reader.ReadString('\n')
		if err != nil {
            //读到末尾
			if err == io.EOF {
				fmt.Println("File read ok!")
				break
			} else {
				fmt.Println("Read file error!", err)
				return
			}
		}
		fmt.Println(line)

        //根据关键词覆盖当前行
		if strings.Contains(line, "address") {
			bytes := []byte("address " + ip + "\n")
			file.WriteAt(bytes, pos)
		} else if strings.Contains(line, "gateway") {
			bytes := []byte("gateway " + gateway + "\n")
			file.WriteAt(bytes, pos)
		} else if strings.Contains(line, "netmask") {
			bytes := []byte("netmask " + netmask + "\n")
			file.WriteAt(bytes, pos)
		}

        //每一行读取完后记录位置
		pos += int64(len(line))
	}
}
复制代码

结果:

$ sudo ./go_build_test_go_linux 
[sudo] xx 的密码: 
file size: 180
# interfaces(5) file used by ifup(8) and ifdown(8)

auto lo

iface lo inet loopback



auto ens33

iface ens33 inet static

address 40.40.40.210

gateway 40.40.40.1

netmask 255.255.255.0

File read ok!
$ cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback

auto ens33
iface ens33 inet static
address 40.40.40.220
gateway 40.40.40.1
netmask 255.255.255.0
复制代码

关键点:每读取一行记录目前的移动位置,然后调用file.WriteAt进行内容覆盖写入(不能是追加方式),因为找到这一行的时候记录的位置正好是上一行的末尾,所以正好覆盖。

4、扩展及目前的缺陷

其实最方便的方式其实是shell脚本的方式,然后通过各种语言都可以调用,并且某些时候也可以单独执行脚本。

其次,对于新内容长度少于旧内容的长度时无法做到全部覆盖,这个时候就稍微麻烦一点,再下下一行的时候或者其余的内容全部读取然后覆盖写入即可。

不明白接口使用的查看一下官方的io包,后面我们也会再专注整理一下io标准库的接口。

缺陷:经测试及各位小伙伴的验证,目前替换的内容少于被替换的内容会造成多出的部分替换不掉;替换的内容多于被替换的内容会导致可能替换到下一行的内容,换行符都被替换了~.~(暂时没有找到更好的方式,只能内容相同长度的内容替换,欢迎大家交流完善)

“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

猜你喜欢

转载自juejin.im/post/7130055277285474341