How can I resolve symlinks in Go?
Currently I call readlink -f but I want something more idiomatic.
package main
import (
"os/exec"
"fmt"
)
func resolve(p string) string {
cmd := exec.Command("readlink", "-f", p)
out, _ := cmd.Output()
return (string(out))
}
func main() {
fmt.Printf(resolve("/initrd.img"))
}
See filepath.EvalSymlinks().
EvalSymlinks returns the path name after the evaluation of any symbolic links. If path is relative the result will be relative to the current directory, unless one of the components is an absolute symbolic link.
Examples
Tree:
/bin/sh -> bash
/usr/lib/libresolv.so -> ../../lib/libresolv.so.2
os.Readlink()
os.Readlink("/bin/sh") // => bash
os.Readlink("/usr/lib/libresolv.so") //=> ../../lib/libresolv.so.2
filepath.EvalSymlinks()
filepath.EvalSymlinks("/bin/sh") // => /bin/bash
filepath.EvalSymlinks("/usr/lib/libresolv.so") //=> /lib/libresolv-2.20.so
Note: not absolute path (cd /bin)
filepath.EvalSymlinks("sh") // => bash
filepath.EvalSymlinks("/bin/sh") // => /bin/bash
And somthing more
filepath.EvalSymlinks("/bin/bash") // => /bin/bash
// but
os.Readlink("/bin/bash") // => error: readlink /bin/bash: invalid argument
Example application not for playground
Use os.Lstat:
func Lstat(name string) (fi FileInfo, err error)
Lstat returns a FileInfo describing the named file. If the file is a symbolic link, the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. If there is an error, it will be of type *PathError.
EDIT:
Then returned os.FileInfo will only allow you to check if 'name' is a link or not (fi.Mode() & os.ModeSymlink != 0). If it is, then use os.Readlink to get the pointee:
func Readlink(name string) (string, error)
Readlink returns the destination of the named symbolic link. If there is an error, it will be of type *PathError.
I had the problem that filepath.EvalSymlinks() prepended /private/ to resolved links that were in /tmp/ on Mac (maybe symlinks are not supposed to be pointing there).
os.Readlink() on the other hand, returned an absolute path on Mac, but a relative path on Ubuntu.
Therefore, to be sure to retrieve an absolute path, the following solution worked for me:
resolvedLink, _ := os.Readlink(someSymlink) // TODO: Handle error here
if !filepath.IsAbs(resolvedLink) { // Output of os.Readlink is OS-dependent...
resolvedLink = path.Join(path.Dir(someSymlink), resolvedLink)
}
Related
As mentioned in GoDocs, os.Create() creates a file in specific path.
os.Create("fonts/foo/font.eot")
But when fonts or foo doesn't exists, it returns panic: open fonts/foo/font.eot: The system cannot find the path specified.
So i used os.MkdirAll() to create nested directory. But there are many other problems with this function.
path := "fonts/foo/font.eot"
// this line create a directory named (font.eot) !
os.MkdirAll(path, os.ModePerm)
Is there any better way to create a file in nested directories?
The standard way is something like this:
func create(p string) (*os.File, error) {
if err := os.MkdirAll(filepath.Dir(p), 0770); err != nil {
return nil, err
}
return os.Create(p)
}
A few notes:
os.Create does not panic as stated in the question.
Create a directory from the directory part of the file path, not the full path.
I am trying to implement the function mkdir in a fuse, written in Go,and I'm using Bazil library. I have successfully implemented a simple read-only fs, and I now want to be able to call mkdir inside any existing directory to make a new one.
I have made sure that all the existing directories are writable,(attr.Mode = os.ModeDir | 0777).
Right now I have just added the function:
func (d Dir) MkDir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
dir := &Dir{name: req.Name, files: 0, inode: 10 /*a random inode*/,mode: os.FileMode(0777),nextdir: nil, nextfile: nil}
d.nextdir = dir
return dir, nil
}
in my own implementation of the hello fs example of Bazil's library. But that doesn't seem to make any difference.
When I call mkdir new_dir_name from the terminal, I get the error: "mkdir: cannot create directory ‘new_dir_name’: Operation not permitted", even though I have added the mkdir function.
Any insights as to why this is happening, and what else should I add to my code to make this working would be great. Also, this is my first stackoverflow question, so I'm sorry if I didn't ask in a clear way.
the correct function for make directory like this
func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
}
I am creating Go command-line app and I need to generate some stuff in the current directory (the directory which the user execute the commands from)
to get the pwd I need to use
os.Getwd()
but this give me path like
/Users/s05333/go/src/appcmd
and I need path like this
/Users/s05333/go/src/
which option I've in this case?
Omit the last string after the / or there is better way in Go?
Take a look at the filepath package, particularly filepath.Dir:
wd,err := os.Getwd()
if err != nil {
panic(err)
}
parent := filepath.Dir(wd)
Per the docs:
Dir returns all but the last element of path, typically the path's directory.
Another option is the path package:
package main
import "path"
func main() {
s := "/Users/s05333/go/src/appcmd"
t := path.Dir(s)
println(t == "/Users/s05333/go/src")
}
https://golang.org/pkg/path#Dir
I've some command line program which I need to read files from parent folder, I mean
-parentDir
-- myproject
--- cmd
----main.go
--otherdir
-file.json
As you can see otherdir is like sibling to myproject and I need from my main.go read the file.json
what I've tried is like following
func visit(path string, f os.FileInfo, err error) error {
fmt.Printf("Visited: %s\n", path)
return nil
}
func main() {
flag.Parse()
root := flag.Arg(0)
err := filepath.Walk(root, visit)
fmt.Printf("filepath.Walk() returned %v\n", err)
}
I've also try to provide args(-1) which doesnt help...
Any idea how from command line program I can read some files that on level up from my executable ?
I've also tried with
import "github.com/mitchellh/go-homedir"
func Path(path string) (error, string) {
home, err := homedir.Dir()
}
this give the root directory which doesnt help either...
It doesn't matter where the binary is, it matters what the working directory is (the directory you're in when you execute the program). All relative paths will be relative to the current working directory. So, if you're executing from myproject, you'd use something like ../ as the root path to Walk.
That said, I would highly recommend you make the path configurable, rather than assuming the binary will always be executed from some particular location within the source tree.
When in a source file $PWD/dir/src.go I use
os.Open("myfile.txt")
it looks for myfile.txt in $PWD (which looks normal).
Is there way to tell Go to look for myfile.txt in the same directory as src.go ? I need something like __FILE__ in Ruby.
Go is not an interpreted language so looking for a file in the same location as the source file doesn't make any sense. The go binary is compiled and the source file doesn't need to be present for the binary to run. Because of that Go doesn't come with an equivalent to FILE. The runtime.Caller function returns the file name at the time the binary was compiled.
I think perhaps if we understood why you actually wanted this functionality we could advise you better.
A possible substitute skeleton:
func __FILE__() (fn string) {
_, fn, _, _ = runtime.Caller(0)
return
}
Details here.
Use package osext
It's providing function ExecutableFolder() that returns an absolute path to folder where the currently running program executable reside (useful for cron jobs). It's cross platform.
Online documentation
package main
import (
"github.com/kardianos/osext"
"fmt"
"log"
)
func main() {
folderPath, err := osext.ExecutableFolder()
if err != nil {
log.Fatal(err)
}
fmt.Println(folderPath)
}
You can also get full executable path (similar to __FILE__):
package main
import (
"github.com/kardianos/osext"
"fmt"
)
func main() {
exeAbsolutePath, _ := osext.Executable()
fmt.Println(exeAbsolutePath)
}