I have the following piece of code which creates an output file on a local drive and required to do the same on a network mapped drive let's call it [H:].
The file name (full path name) entered from command line as argument[1].
I am using Windows 10/Server 2016
// The following will create and append to the file when required.
sourcefile, errf := os.OpenFile(os.Args[1], s.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if erro != nil {
panic(erro)
}
defer outfile.Close()
I use the following function to write a map into this file.
func map2Linpro(inp map[string][]string, outfile io.Writer) {
for k, v := range inp {
_, err := fmt.Fprintf(outfile, "%s %s=%s %s\n", v[0], k, v[1], v[2])
if err != nil {
fmt.Println("Error Writing to File: ", err)
}
}
}
Everything is working just fine if the output file is on the local Drive, but when using full path with the Mapped Drive letter, I received the following error:
Error: write h://00_sc//dest01.txt: The parameter is incorrect.
I searched for any reason, but could not find one.
I would appreciate if someone help
The following is the Error I got after adding Panic(erro) after OpenFile.
Which proves that the error source is fmt.Fprintf
Error Writing to File: write H:/00_sc/dest01.txt: The parameter is incorrect.
Thanks to all.
outfile, _ := os.OpenFile(os.Args[2], os.O_CREATE|os.O_APPEND, 0666)
should read
outfile, err := os.OpenFile(os.Args[2], os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
rewrite those lines and the resulting error message should give a clue as to the cause
Related
I'm trying to execute .ics file that my program just created. Basically, my program is simple CLI calendar app, which generates .ics file. It would be nice if my program would execute this file and add it straight to OS calendar app, without unnecessary searching and executing through OS GUI.
I paste main function to better understanding.
func main() {
serialized, name := cal()
f, err := os.Create(name + ".ics")
if err != nil {
log.Fatal(err)
}
defer f.Close()
_, err2 := f.WriteString(serialized)
if err2 != nil {
log.Fatal(err2)
}
cmd := exec.Command(name + ".ics")
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
}
As it's shown I tried with exec.Command, but it doesnt' work. I was even trying with additional prefixes like ./ or ~/, but it as well didn't work.
Error messages:
fork/exec ./Meeting.ics: permission denied
exec: "Meeting.ics": executable file not found in $PATH
So to sum it up - I want to skip the process where the user has to find a file and open it. I want to make it automatically as a part of my application.
Here's my repository if it would help https://github.com/lenoopaleno/golang-calendar
I'm working on WSL 2, Ubuntu 22.04
Beside the comments above, you might have a problem in your code with the defer f.Close()
The defer runs when the function ends. Until that time your file might or might not be closed and accessible by a different process.
Second you will most likely have to set an execute flag on the a program to run under unix style operating systems.
Program adjustment:
func main() {
serialized, name := cal()
f, err := os.Create(name + ".ics")
if err != nil {
log.Fatal(err)
}
_, err2 := f.WriteString(serialized)
if err2 != nil {
log.Fatal(err2)
}
f.Sync()
f.Close()
exec.Command(`chmod +x `+name+".ics").Run() // This can be done prettier
cmd := exec.Command(name + ".ics")
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
}
I have a 7z archive of a number of .txt files. I am trying to list all the files in the archive and upload them to an s3 bucket. But I'm having trouble with extracting .7z archives on Go. To do this, I found a package github.com/gen2brain/go-unarr (imported as extractor) and this is what I have so far
content, err := ioutil.ReadFile("sample_archive.7z")
if err != nil {
fmt.Printf("err: %+v", err)
}
a, err := extractor.NewArchiveFromMemory(content)
if err != nil {
fmt.Printf("err: %+v", err)
}
lst, _ := a.List()
fmt.Printf("lst: %+v", last)
This prints a list of all the files in the archive. But this has two issues.
It reads files from local using ioutil and the input of NewArchiveFromMemory must be of type []byte. But I can't read from local and will have to use a file from memory of type os.file. So I will either have to find a different method or convert the os.file to []byte. There's another method NewArchiveFromReader(r io.Reader). But this is returning an error saying Bad File Descriptor.
file, err := os.OpenFile(
path,
os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
0666,
)
a, err := extractor.NewArchiveFromReader(file)
if err != nil {
fmt.Printf("ERROR: %+v", err)
}
lst, _ := a.List()
fmt.Printf("files: %+v\n", lst)
I am able to get the list of the files in the archive. And using Extract(destinaltion_path string), I can also extract it to a local directory. But I want the extracted files also in os.file format ( ie. a list of os.file since there will be multiple files ).
How can I change my current code to achieve both the above targets? Is there any other library to do this?
os.File implements the io.Reader interface (because it has a Read([]byte) (int, error) method defined), so you can use NewArchiveFromReader(file) without any conversions needed. You can read up on Go interfaces for more background on why that works.
If you're okay with extracting to a local directory, you can do that and then read the files back in (warning, may contain typos):
func extractAndOpenAll(*extractor.Archive) ([]*os.File, error) {
err := a.Extract("/tmp/path") // consider using ioutil.TempDir()
if err != nil {
return nil, err
}
filestats, err := ioutil.ReadDir("/tmp/path")
if err != nil {
return nil, err
}
# warning: all these file handles must be closed by the caller,
# which is why even the error case here returns the list of files.
# if you forget, your process might leak file handles.
files := make([]*os.File, 0)
for _, fs := range(filestats) {
file, err := os.Open(fs.Name())
if err != nil {
return files, err
}
files = append(files, file)
}
return files, nil
}
It is possible to use the archived files without writing back to disk (https://github.com/gen2brain/go-unarr#read-all-entries-from-archive), but whether or not you should do that instead depends on what your next step is.
Sorry for reformatting my question by focusing on the real issue as follows:
I am trying to create file and write to it on a network mapped drive, which I can access, create, delete and edit files using windows explorer or CMD (Windows 10/Server 2016).
The following code should do the task:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
//The following is the file name on a network mapped drive H:
out, errc := os.OpenFile("H:/00_SC/Dest01.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if errc != nil {
fmt.Println("Error Creating/Wrting to Dest file :", errc)
}
defer out.Close()
wr := bufio.NewWriter(out)
mystring := "another line here"
d, err := wr.WriteString(mystring)
if err != nil {
fmt.Println("Error writing by Writer: ", err)
}
errflush := wr.Flush()
if errflush != nil {
fmt.Println("Error Flushing the write to file", errflush)
}
wrtd, errw := out.Write([]byte("Write something in the file"))
if errw != nil {
fmt.Println("Error of Writte call", errw)
}
fmt.Println("Length of mystring = ", len(mystring))
fmt.Println("Bytes written to buffer = ", d)
fd, errf := fmt.Fprintf(out, "Another line to the file using Fprintf %d", d)
if errf != nil {
fmt.Println("Fprintf Error: ", errf)
}
fmt.Println("Bytes written to file by Fprintf = ", fd)
fmt.Println("Bytes written to file using Write", wrtd)
}
The file was created successfully, however, neither of the methods used write to it.
Bufio.WriteString neither write nor return an error !!
Fprintf failed with unclear error message which I searched and could not find a meaning or reason.
Here the output I got when running the compiled file:
Error Flushing the write to file write H:/00_SC/Dest01.txt: The parameter is incorrect.
Error of Write call write H:/00_SC/Dest01.txt: The parameter is incorrect.
Length of mystring = 17
Bytes writen to buffer = 17
Fprintf Error: write H:/00_SC/Dest01.txt: The parameter is incorrect.
Bytes written to file by Fprintf = 0
Bytes written to file using Write 0
I would appreciate your help and guidance finding the cause of this error.
Thanks
I am trying to write in newly generated file in a specific attached volume to my container (a directory)
however i am not sure about the correct the syntax. Below my code:
// Write the certificates to disk
f, _ := os.Create(filepath.Join("/data/certs/", "chamscertificate.pem"))
f.Write(cert)
f.Close()
f, _ = os.Create("key.pem")
f.Write(key)
f.Close()
}
when executing "go run .", i get the "key.pem" but not the "certificate.pem".
You don't check for errors. If the file wasn't created, the information about why the file wasn't created will be in the error return value from os.Create.
f, err := os.Create(filepath.Join("/data/certs/", "chamscertificate.pem"))
if err != nil {
log.Fatal(err)
}
... etc.
Note that f.Write and f.Close also return errors that should be checked.
fsync doc states
Calling fsync() does not necessarily ensure that the entry in the directory containing the file has also reached disk. For that an explicit fsync() on a file descriptor for the directory is
also needed.
I'm trying to sync a directory to the network mapped drive using SMB on windows, similar to what fsync does on Linux.
The following go code snippet which works fine if the directory is stored on a local drive but fails if it is on a network mapped folder.
func main() {
dir := "Z:\\smb-test" // Path to network mapped drive
f, err := openDir(dir)
if err != nil {
log.Fatal(err)
}
// Works fine if the path is located on a local disk but
// fails if the directory is on a network mapped drive
if err := f.Sync(); err != nil {
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
func openDir(path string) (*os.File, error) {
fd, err := openDirWin(path)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), path), nil
}
func openDirWin(path string) (fd syscall.Handle, err error) {
pathp, err := syscall.UTF16PtrFromString(path)
if err != nil {
return syscall.InvalidHandle, err
}
access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE)
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
createmode := uint32(syscall.OPEN_EXISTING)
fl := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS)
return syscall.CreateFile(pathp, access, sharemode, nil, createmode, fl, 0)
}
The program fails with
Z:\\smb-test Incorrect function.
MSDN states that the handle passed in should either be a handle to a file, or to a volume, but says nothing about directories.
https://learn.microsoft.com/en-us/windows/desktop/api/FileAPI/nf-fileapi-flushfilebuffers
And FlushFileBuffers is not listed as a function that accepts directory handles
https://learn.microsoft.com/en-us/windows/desktop/fileio/obtaining-a-handle-to-a-directory
I also noticed the flushfilebuffers system call fails with Invalid Device Request for a network mapped drive
So the question is how do I sync a directory on windows?
Or is window's buffered I/O fundamentally different from POSIX and we don't need to flush a directory when a file inside it is modified?
The short answer to my question is there's no way to sync directories on windows using system calls.FlushFileBuffers works for files and volumes, NOT Directories.
Read https://social.msdn.microsoft.com/Forums/Windowsdesktop/en-US/847a735b-f21a-4be2-880b-12660e5b98b4/flushfilebuffers-system-call-fails-on-network-mapped-drive and
https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/0e0b734b-2b73-414d-8833-8d2eed7043f6/sync-directories-on-windows
for more details.