How to overwrite file content in golang - go

I have a empty file called a.txt, I want to output a value(int) to it in a loop, and overwrite last content in file a.txt. For example,
// open a file
f, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// another file
af, err := os.OpenFile("a.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
log.Fatal(err)
}
defer af.Close()
b := []byte{}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
b = append(b, scanner.Bytes()...)
// how to output len(b) into a.txt?
}

You can also try:
os.OpenFile with custom flags to truncate file, as shown below
package main
import (
"log"
"os"
)
func main() {
f, err := os.OpenFile("notes.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}

Just use the truncate method and write again to file starting at the begining.
err = f.Truncate(0)
_, err = f.Seek(0, 0)
_, err = fmt.Fprintf(f, "%d", len(b))

Use os.Create() instead:
f, err := os.Create("test.txt")
From the func's doc:
Create creates or truncates the named file. If the file already exists, it is truncated. If the file does not exist, it is created ...

Related

Go: How to read in MRT (.bz2) file as byte and parse data

I am trying to read in an mrt (with .bz2 file extension) from archive.routeviews.org namely file - http://archive.routeviews.org/route-views.chile/bgpdata/2022.05/UPDATES/updates.20220501.0000.bz2.
I have found some code online that parses it using three different packages - FGBGP, go-mrt, goBGP. Here is the code:
package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
mrt1 "github.com/cloudflare/fgbgp/mrt"
mrt2 "github.com/kaorimatz/go-mrt"
mrt3 "github.com/osrg/gobgp/pkg/packet/mrt"
)
func main() {
data, err := os.ReadFile("updates.20220501.0000")
if err != nil {
log.Fatal(err)
}
// or paste bytes instead data := []byte{}
fmt.Println("FGBGP")
rdr := bytes.NewBuffer(data)
r, err := mrt1.DecodeSingle(rdr)
for r != nil && err == nil {
fmt.Println(r)
r, err = mrt1.DecodeSingle(rdr)
}
fmt.Println("Go-mrt")
rdr2 := mrt2.NewReader(bytes.NewBuffer(data))
r2, err := rdr2.Next()
for r2 != nil && err == nil {
fmt.Println(r2)
r2, err = rdr2.Next()
}
fmt.Println("GoBGP")
sc := bufio.NewScanner(bytes.NewBuffer(data))
sc.Split(mrt3.SplitMrt)
for {
b := sc.Scan()
if !b {
break
}
mrtb := sc.Bytes()
hdr, err := mrt3.NewMRTHeader(0, mrt3.BGP4MP, mrt3.RIB_IPV4_UNICAST, 0)
if err != nil {
fmt.Println(err)
break
}
hdr.DecodeFromBytes(mrtb)
r3, err := mrt3.ParseMRTBody(hdr, mrtb[mrt3.MRT_COMMON_HEADER_LEN:])
if err != nil {
fmt.Println(err)
}
fmt.Println(r3)
}
}
When this is run, FGBGP does not input anything, go-mrt does seem to output the lines but each line seems to be missing things I would like to see such as AS PATH etc. And goBGP which was the one I was most interested in, throws an unsupported type error.
Here is an example output for go-mrt and goBGP:
go-mrt: &{{{0 63786960781 <nil>} 17 4} 27678 6447 0 1 200.16.114.34 200.16.114.60 0x140003f5900}
goBGP: <nil> unsupported type: 17

How to merge or combine 2 files into single file

I need to take tmp1.zip and append it's tmp1.signed file to the end of it; creating a new tmp1.zip.signed file using Go.
It's essentially same as cat | sc
I could call cmd line from Go, but that seems super inefficient (and cheesy).
So far
Google-ing the words "go combine files" et. al. yields minimal help.
But I have come across a couple of options that I have tried such as ..
f, err := os.OpenFile("tmp1.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
if _, err := f.Write([]byte("appended some data\n")); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
But that is just getting strings added to the end of the file, not really merging the two files, or appending the signature to the original file.
Question
Assuming I am asking the right questions to get one file appended to another, Is there a better sample of how exactly to merge two files into one using Go?
Based on your question, you want to create a new file with the content of both files.
You can use io.Copy to achieve that.
Here is a simple command-line tool implementing it.
package main
import (
"io"
"log"
"os"
)
func main() {
if len(os.Args) != 4 {
log.Fatalln("Usage: %s <zip> <signed> <output>\n", os.Args[0])
}
zipName, signedName, output := os.Args[1], os.Args[2], os.Args[3]
zipIn, err := os.Open(zipName)
if err != nil {
log.Fatalln("failed to open zip for reading:", err)
}
defer zipIn.Close()
signedIn, err := os.Open(signedName)
if err != nil {
log.Fatalln("failed to open signed for reading:", err)
}
defer signedIn.Close()
out, err := os.OpenFile(output, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalln("failed to open outpout file:", err)
}
defer out.Close()
n, err := io.Copy(out, zipIn)
if err != nil {
log.Fatalln("failed to append zip file to output:", err)
}
log.Printf("wrote %d bytes of %s to %s\n", n, zipName, output)
n, err = io.Copy(out, signedIn)
if err != nil {
log.Fatalln("failed to append signed file to output:", err)
}
log.Printf("wrote %d bytes of %s to %s\n", n, signedName, output)
}
Basically, it open both files you want to merge, create a new one and copy the content of each file to the new file.

How can I assign a return value to a function input via short variable declaration in Golang?

Consider following code snippet...
f, err := os.OpenFile(".", os.O_RDONLY, 0666)
In-order to check for error & proceed, we need to have something like..
func checkerr(err error) {
if err != nil {
panic(err)
}
}
and then call this tiny function to validate the error. For eg..
f, err := os.OpenFile(".", os.O_RDONLY, 0666)
checkerr(err)
files, err := f.Readdirnames(0)
checkerr(err)
While this works, I'm looking for a shortcut to directly call checkerr() during initialisation phase itself. See below...
f, checkerr(err) := os.OpenFile(".", os.O_RDONLY, 0666)
Is it possible using native Golang constructs?
Repeat after me: "It's idiomatic Go to check for errors." The following is not bad code, even if it's not the DRYest.
f, err := os.OpenFile(".", os.O_RDONLY, 0666)
if err != nil {
panic(err)
}
files, err := f.Readdirnames(0)
if err != nil {
panic(err)
}

exec.Command with input redirection

I'm trying to run a fairly simple bash command from my Go code. My program writes out an IPTables config file and I need to issue a command to make IPTables refresh from this config. This is very straightforward at the commandline:
/sbin/iptables-restore < /etc/iptables.conf
However, I can't for the life of me figure out how to issue this command with exec.Command(). I tried a few things to accomplish this:
cmd := exec.Command("/sbin/iptables-restore", "<", "/etc/iptables.conf")
// And also
cmd := exec.Command("/sbin/iptables-restore", "< /etc/iptables.conf")
No surprise, neither of those worked. I also tried to feed the filename into the command by piping in the file name to stdin:
cmd := exec.Command("/sbin/iptables-restore")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
err = cmd.Start()
if err != nil {
log.Fatal(err)
}
io.WriteString(stdin, "/etc/iptables.conf")
That doesn't work either, no surprise. I can use stdin to pipe in the contents of the file, but this seems silly when I can just tell iptables-restore what data to go read. So how might I get Go to run the command /sbin/iptables-restore < /etc/iptables.conf?
first read this /etc/iptables.conf file content then write it to cmd.StdinPipe() like this:
package main
import (
"io"
"io/ioutil"
"log"
"os/exec"
)
func main() {
bytes, err := ioutil.ReadFile("/etc/iptables.conf")
if err != nil {
log.Fatal(err)
}
cmd := exec.Command("/sbin/iptables-restore")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
err = cmd.Start()
if err != nil {
log.Fatal(err)
}
_, err = io.WriteString(stdin, string(bytes))
if err != nil {
log.Fatal(err)
}
}
cmd := exec.Command("/usr/sbin/iptables-restore", "--binary", iptablesFilePath)
_, err := cmd.CombinedOutput()
if err != nil {
return err
}
return nil
this work fine on my Raspberry Pi3
The os/exec package does not invoke the system shell, nor does it implement the < redirection syntax typically handled by a shell.
Open the input file and use that file as stdin:
stdin, err := os.Open("/etc/iptables.conf")
if err != nil {
log.Fatal(err)
}
defer stdin.Close()
cmd := exec.Command("/sbin/iptables-restore")
cmd.Stdin = stdin // <-- use open file as stdin
result, err := cmd.CombinedOutput()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", result)

How to write to a file in golang

i am trying to write to to a file. i read the whole content of the file and now i want to change the content of the file based on some word that i have got from the file. but when i check, the content of the file, it is still the same and it has not change. this is what i used
if strings.Contains(string(read), sam) {
fmt.Println("this file contain that word")
temp := strings.ToUpper(sam)
fmt.Println(temp)
err := ioutil.WriteFile(fi.Name(), []byte(temp), 0644)
} else {
fmt.Println(" the word is not in the file")
}
Considering that your call to ioutil.WriteFile() is consistent with what is used in "Go by Example: Writing Files", this should work.
But that Go by example article check the err just after the write call.
You check the err outside the scope of your test:
if matched {
read, err := ioutil.ReadFile(path)
//fmt.Println(string(read))
fmt.Println(" This is the name of the file", fi.Name())
if strings.Contains(string(read), sam) {
fmt.Println("this file contain that word")
Value := strings.ToUpper(sam)
fmt.Println(Value)
err = ioutil.WriteFile(fi.Name(), []byte(Value), 0644)
} else {
fmt.Println(" the word is not in the file")
}
check(err) <===== too late
}
The err you are testing is the one you got when reading the file (ioutil.ReadFile), because of blocks and scope.
You need to check the error right after the Write call
err = ioutil.WriteFile(fi.Name(), []byte(Value), 0644)
check(err) <===== too late
Since WriteFile overwrite the all file, you could strings.Replace() to replace your word by its upper case equivalent:
r := string(read)
r = strings.Replace(r, sam, strings.ToUpper(sam), -1)
err := ioutil.WriteFile(fi.Name(), []byte(r), 0644)
For a replace which is case insensitive, use a regexp as in "How do I do a case insensitive regular expression in Go?".
The, use func (*Regexp) ReplaceAllString:
re := regexp.MustCompile("(?i)\\b"+sam+"\\b")
r = re.ReplaceAllString(r, strings.ToUpper(sam))
err := ioutil.WriteFile(fi.Name(), []byte(r), 0644)
Note the \b: word boundary to find the any word starting and ending with sam content (instead of finding substrings containing sam content).
If you want to replace substrings, simply drop the \b:
re := regexp.MustCompile("(?i)"+sam)
It's not clear what you want to do. My best guess is something like this:
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
)
func UpdateWord(filename string, data, word []byte) (int, error) {
n := 0
f, err := os.OpenFile(filename, os.O_WRONLY, 0644)
if err != nil {
return n, err
}
uWord := bytes.ToUpper(word)
if len(word) < len(uWord) {
err := errors.New("Upper case longer than lower case:" + string(word))
return n, err
}
if len(word) > len(uWord) {
uWord = append(uWord, bytes.Repeat([]byte{' '}, len(word))...)[:len(word)]
}
off := int64(0)
for {
i := bytes.Index(data[off:], word)
if i < 0 {
break
}
off += int64(i)
_, err = f.WriteAt(uWord, off)
if err != nil {
return n, err
}
n++
off += int64(len(word))
}
f.Close()
if err != nil {
return n, err
}
return n, nil
}
func main() {
// Test file
filename := `ltoucase.txt`
// Create test file
lcase := []byte(`update a bc def ghij update klmno pqrstu update vwxyz update`)
perm := os.FileMode(0644)
err := ioutil.WriteFile(filename, lcase, perm)
if err != nil {
fmt.Println(err)
return
}
// Read test file
data, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(data))
// Update word in test file
word := []byte("update")
n, err := UpdateWord(filename, data, word)
if err != nil {
fmt.Println(n, err)
return
}
fmt.Println(filename, string(word), n)
data, err = ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(data))
}
Output:
update a bc def ghij update klmno pqrstu update vwxyz update
ltoucase.txt update 4
UPDATE a bc def ghij UPDATE klmno pqrstu UPDATE vwxyz UPDATE

Resources