Go replaces (overwrites) a line of the file

Posted by alwoodman on Mon, 07 Mar 2022 23:47:51 +0100

Go replaces (overwrites) a line of the file

1. Foreword

There is such a requirement. After finding a line of content with a keyword in the file, we replace the line with the new content we need, such as modifying the network configuration file, modifying the picture address, modifying all keywords in the code, etc., which is similar to the keyword replacement function in the editor, but we judge the file directly.

2. The idea of overwriting the content of a line of file

  • 1. Open file
  • 2. Read each line of the file
  • 3. Judge whether the line needs to be covered according to the keyword. If yes, write the content from the beginning of the line to make it cover the old content of the line

Because it is coverage, we have a premise that the length of the newly written content needs to be greater than or equal to the length of the old content. When the new content is less than the old content, we will try again in the expansion. The basic ideas include two: 1. Write empty content to cover the excess position (it should not be possible, you can try); 2. After the new content is written, the line feed is directly added, and then the remaining content of the previous file is overwritten. It is still the idea of overwriting.

3. Implement a code example that covers a line of content

Let's take modifying the network configuration file in my virtual machine as an example (remember to back up first):

Content of original Ubuntu configuration file:

$ 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

Then we modify the contents of the last three lines through keywords such as address, gateway and netmask to modify the ip address, gateway and subnet mask in the configuration file.

Code content:

package main

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

func main() {
    //Open file in read-write mode
	file, err := os.OpenFile("/etc/network/interfaces", os.O_RDWR, 0666)
	if err != nil {
		fmt.Println("open file filed.", err)
		return
	}
    //defer close file
	defer file.Close()

    //Get file size
	stat, err := file.Stat()
	if err != nil {
		panic(err)
	}
	var size = stat.Size()
	fmt.Println("file size:", size)

    //Read file contents into io
	reader := bufio.NewReader(file)
	pos := int64(0)
	ip := "40.40.40.220"
	gateway := "40.40.40.1"
	netmask := "255.255.255.0"
	for {
        //Read each line
		line, err := reader.ReadString('\n')
		if err != nil {
            //Read to the end
			if err == io.EOF {
				fmt.Println("File read ok!")
				break
			} else {
				fmt.Println("Read file error!", err)
				return
			}
		}
		fmt.Println(line)

        //Overwrite the current line according to keywords
		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)
		}

        //Record the position after reading each line
		pos += int64(len(line))
	}
}

result:

$ sudo ./go_build_test_go_linux 
[sudo] xx Password for: 
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

The key point is to read the current location of each row and then call file.. Writeat overwrites the content (cannot be appended), because the position of the record when finding this line is just the end of the previous line, so it is overwritten.

4. Expand

In fact, the most convenient way is the shell script, which can be called through various languages, and sometimes the script can be executed separately.

Secondly, when the length of the new content is less than the length of the old content, you can't cover it all. At this time, it's a little troublesome. When you go to the next line or read all the rest of the content and then write it.

If you don't understand the interface, please check the official io package. Later, we will also focus on sorting out the interfaces of the io standard library.

Topics: Go