Is it possible to get the path of system folders in Go, in a cross-platform way? eg. temp folders, "document" folders, etc.
I found ioutil.TempFolder/File but they do something different. Any idea?
There's currently no way to access standard system folders in a cross-platform way. The Home directory though can be access using the user package:
u, _ := user.Current()
fmt.Println(u.HomeDir)
In the year 2020, I am trying to get similar things, but only for temporary directory cross-platform. When I found this thread and read some answers, I almost make a conclusion that it is not possible.
But after a few further research, I found that go already have it. Just like pointed by the accepted answer, it stands inside os package. Based on this documentation: https://golang.org/pkg/os/#TempDir, we can get it by calling: TempDir() function.
If someone trying to look at another OS system directories path, and stumbled upon in this thread, my suggestion, please just try to have a few further research. Looks like currently go have more complete functions regarding OS system directories.
A built-in option doesn't exist yet. Your best bet is to open an issue and submit a feature request.
In the meantime you can add support yourself by using platform specific +build flags. With that you have a couple of options:
Use the os package to get the information for each system, possibly through the shell.
Use cgo with existing C / C++ methods. See this answer, which explains how to get this information using C++ for Windows.
It may also be helpful to read the source code of the os package to see how platform-specific information is obtained. This could help you devise a way to get this information, and perhaps submit a patch to be included.
For the OS's temp directory, as stated by Bayu, there is a built-in function os.TempDir() string to get the os specific temp directory:
// TempDir returns the default directory to use for temporary files.
//
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp.
// On Windows, it uses GetTempPath, returning the first non-empty
// value from %TMP%, %TEMP%, %USERPROFILE%, or the Windows directory.
// On Plan 9, it returns /tmp.
//
// The directory is neither guaranteed to exist nor have accessible
// permissions.
func TempDir() string {
return tempDir()
}
which is actually used by the ioutil.TempDir(dir, pattern string) (string, error) function if you provide an empty string for the dir parameter. Check out the 5th and 6th lines:
// TempDir creates a new temporary directory in the directory dir.
// The directory name is generated by taking pattern and applying a
// random string to the end. If pattern includes a "*", the random string
// replaces the last "*". TempDir returns the name of the new directory.
// If dir is the empty string, TempDir uses the
// default directory for temporary files (see os.TempDir).
// Multiple programs calling TempDir simultaneously
// will not choose the same directory. It is the caller's responsibility
// to remove the directory when no longer needed.
func TempDir(dir, pattern string) (name string, err error) {
Besides the methods Luke mentioned, on Windows you can get some of the paths from environment variables. Same applies, to some extent, to Unix ($HOME, etc.).
Related
ioutil.WriteFile takes a perm argument - if the file to write doesn't already exist, it is created with permissions perm:
func WriteFile(filename string, data []byte, perm os.FileMode) error
In the general case, is there a recommended value to pass for the perm argument?
More specifically, I am writing a file which is a transformation of an existing file. Is it recommended to read the permissions of the input file (using os.Stat) and use the same permissions for the output file?
In the general case, is there a recommended value to pass for the perm argument?
0666. This is the value used by Go's os.Create, and is also the value of MODE_RW_UGO, used when a file is created by tools such as touch.
More specifically, I am writing a file which is a transformation of an existing file. Is it recommended to read the permissions of the input file (using os.Stat) and use the same permissions for the output file?
Other than pure cp, tools don't seem to do this. As above, tools that create new files generally just use 0666.
There is no standard permission that's "generally recommended". This is more of an OS question than a Go question.
Do you want your file to be executable?
Do you want it to be writable?
Do you want other accounts to be able to access it and with which permissions
If you're taking an existing file and transforming it to the output file, and it's logically OK for your application to have both files with the same permissions, then copying permissions sounds like a reasonable idea (unless you foresee security issues).
I need to reference patients.json from patients.go, here's the folder structure:
If I do:
filepath.Abs("../../conf/patients.json")
it works for go test ./... but fails for revel run
If I do:
filepath.Abs("conf/patients.json")
the exact opposite happens (revel is fine but tests fail).
Is there a way to correctly reference the file so that it works both for tests and normal program run?
Relative paths are always interpreted / resolved to a base path: the current or working directory - therefore it will always have its limitations.
If you can live with always taking care of the proper working directory, you may keep using relative paths.
What I would suggest is to not rely on the working directory, but an explicitly specified base path. This may have a default value hard-coded in your application (which may be the working directory as well), and you should provide several ways to override its value.
Recommended ways to override the base path to which your "relative" paths are resolved against:
Command line flag (see flag package)
Environment variable (see os.Getenv())
(Fix named) Config file in user's home directory (see os/user/User and os/user/Current())
Once you have the base path, you can get the full path by joining the base path and the relative path. You may use path.Join() or filepath.Join(), e.g.:
// Get base path, from any or from the combination of the above mentioned solutions
base := "/var/myapp"
// Relative path, resource to read/write from:
relf := "conf/patients.json"
// Full path that identifies the resource:
full := filepath.Join(base, relf) // full will be "/var/myapp/conf/patients.json"
I've never used Revel myself but the following looks helpful to me:
http://revel.github.io/docs/godoc/revel.html
revel.BasePath
revel.AppPath
This is not the problem with path, but the problem with your design.
You should design your code more careful.
As far as I can tell, you share same path in your test file and reveal run. I guess that maybe you hard code your json path in your model package which is not suggested.
Better way is
model package get json path from global config, or init model with json path like model := NewModel(config_path). so reveal run can init model with any json you want.
hard code "../../conf/patients.json" in your xxxx_testing.go
Using Go, I was writing a small utility that in part needs to notice if the filename of an open file changes. The below code illustrates the approach I tried:
package main
import "os"
import "fmt"
import "time"
func main() {
path := "data.txt"
file, _ := os.Open(path)
for {
details, _ := file.Stat()
fmt.Println(details.Name())
time.Sleep(5 * time.Second)
}
}
This just starts an endless loop, running file.Stat() to obtain file details every 5 seconds and then printing out the name. However, despite changing the filename as this is running, the output of the above does not change.
replacing details.Name() with details.Size() does however notice changes to the filesize.
Is this simply a bug in my version of Go, or am I just doing things wrong? I cannot find mention of such an issue anywhere offhand.
I am running this on a Mac, with Go version 1.1.1 (darwin/amd64).
Thanks in advance for any replies :)
On Unix-like operating systems, once you open a file it is no longer tied to a name. The file-descripter you get is only tied to the inode of the file you open. All meta-data of a file is associated with the inode. There is no simple way to get the name of a file from the inode, thus what you want is impossible, except if the operating system you are using provides a special means to do that.
Also: What filename do you want to observe? A file could have multiple names or no name at all (such as a typical temporary file).
The only thing I think you could do is:
Open the file, remember the filename
periodically call Stat on the filename to see whether the inode matches
if the inode is different, your file has been moved
This does however, not give you the new filename. Such a thing is impossible to do portably.
Alas it is quite a difficult problem to find the name of an open file in general.
All file.Name() does is this (see the docs)
// Name returns the name of the file as presented to Open.
func (f *File) Name() string { return f.name }
You might want to check out this question for some ideas: Getting Filename from file descriptor in C. There are solutions for Linux, Windows and Mac in the answers.
I need to get a safe temp folder where I could store temporary files for my application, but so far my research has lead me to conclusion that all approaches I've found are flawed.
The first idea was to use GetTempPath function, but that causes two problems:
The folder might not exist, so I would have to truncate folders one by one up to root, and recreate them if they do not exist back to full path (error prone, tedious)
From "Larry Osterman's WebLog" click it seems that GetTempPath might fallback to USERPROFILE or Windows directory and extract whole lot of files right in there, which is SUPER BAD(TM)!
In the same post, there is a suggestion to use GetEnvironmentVariable, but this seems a dangerous function to me (missing TMP & TEMP envvars for instance).
Is there a cleaner function I could use? Seems that SHGetKnownFolderPath has no clue what temp folder is.
Your program is probably not the only one to rely on GetTempPath, so it's reasonable to expect it to return a proper writable path. Especially since Windows automatically initializes the TMP and TEMP environment variables for you; someone would have to go to some trouble to override them, and it would be their responsibility to make sure the change did not mess up their system.
I would go ahead and assume GetTempPath works properly, and worry about failures when you try to create the temporary file - there are other errors that might occur at that time that you need to check for anyway.
An idea would be to get the path where your application is (GetModuleFileNameEx combined with GetModuleHandle(NULL) and GetCurrentProcess) since this directory cannot be deleted under windows as long as your application is running from it (maybe I'm wrong ...some years ago I couldn't do this :) ) and in this directory create a temporary directory.
Your first bullet point is the solution. Wrap it up in a method so that you don't duplicate code.
According to this answer, Boost's Filesystem library can be used for this.
I am busy writing an application that runs under windows
Where is the correct place to save temporary files ?
If you are using .NET, please use Path.GetTempPath(). This will guarantee that you use the temporary directory assigned to the user that runs your application, regardless of where it is stored.
If you browse the file system, you will notice that there are many "temp" directories:
~\Temp
~\Windows\Temp
~\Users\userName\AppData\Local\Temp
... and many more. Some of these paths are OS-dependent, and won't be present on certain windows flavors. So, save yourself some time and hassle, and let the .NET framework figure out where the "temp" path is located.
Use GetTempPath and and possibly GetTempFileName to determine where to put your temporary files. This is the most reliable, enduser-friendly, and future proof way to get a temporary location for files.
In the temp directory?
Use GetTempPath or in a batch file %TEMP%
C:\Temp is NOT a good choice.
If you are using .Net use code like this:
string baseFolder = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string cfgFolder = Path.Combine(baseFolder, "MyAppName");
try
{
if (!Directory.Exists(cfgFolder))
{
Directory.CreateDirectory(cfgFolder);
}
}
catch { } // If no access, not much we can do.
to get a place for medium-term storage of app data, or Path.GetTempPath() for transient storage of data.
Use the GetTempPath API, or the equivalent for your programming environment.
It depends on the language you are using:
string tempFolder = System.IO.Path.GetTempPath();
will return you the appropriate folder in C# for instance.
or, the environment variables TEMP or TMP if you must.
C:\Documents and Settings\username\Application Data\IsolatedStorage