简体   繁体   中英

Resolving broken symbolic links

I want my Go program to resolve the targets of a few symlinks. The only trouble is these symlinks will be pointing to destinations that do not actually exist, which is fine because I do some filepath manipulations afterwards. Is this even possible? If I do a "readlink -f ", I do get a nil result with exit code 1.

// path is the filepath of a symlink
// this symlink will most likely be pointing to a non-existent file/location
// this is fine and is handled elsewhere
func handleSymlink(path *string) (bool, error) {
    f, err := os.Lstat(*path)
    if err != nil {
        return false, err
    }

    // if f is nil, then this is not a symlink
    if f == nil {
        return false, nil
    }

    ln, err := os.Readlink(*path)
    //ln, err := filepath.EvalSymlinks(*path)

    // ignore IsNotExist errors...this is expected
    if !os.IsNotExist(err) {
        return false, err
    }

    path = &ln
    return true, nil 
} 

Use os.Lstat func:

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.

So you can use returned os.FileInfo value to check whether it's a symlink or not.

fileInfo.Mode()&os.ModeSymlink != 0

At this point you know that given path is symlink.


If you would like to get the actual path of symlink that is pointing to, use

filepath.EvalSymlinks(path)

Your updated code:

func handleSymlink(path *string) (bool, error) {
    fileInfo, err := os.Lstat(*path)
    if os.IsNotExist(err) || err != nil {
        return false, err
    }

    // Check path is symlink or not
    if fileInfo.Mode()&os.ModeSymlink != 0 {
        actualPath, err := filepath.EvalSymlinks(*path)
        if err != nil {
            // Extract actual path from error msg
            apath := strings.Split(err.Error(), ":")[0]
            *path = strings.Split(apath, " ")[1]
            return false, err
        }
        *path = actualPath
        return true, nil
    }

    return false, nil
}

func main() {
    path := "/Users/jeeva/test1"
    result, err := handleSymlink(&path)
    fmt.Println("Error:", err)
    fmt.Println("Result:", result)
    fmt.Println("Actual Path:", path)
}

Let's say we have following paths:

/Users/jeeva/test1 (symlink, pointing to /Users/jeeva/actualpath)
/Users/jeeva/actualpath
/Users/jeeva/actualpath2

If given path exists and it's a symlink:

#Input:
/Users/jeeva/test1

#Output:
Error: <nil>
Result: true
Actual Path: /Users/jeeva/actualpath

If given path is not exists:

#Input:
/Users/jeeva/test1

#Output:
Error lstat /Users/jeeva/notexists: no such file or directory
Result: false
Actual Path: /Users/jeeva/notexists

If given path is exists and it's not a symlink:

#Input:
/Users/jeeva/actualpath2

#Output:
Error <nil>
Result: false
Actual Path: /Users/jeeva/actualpath2

You want the Unix system call readlink , which is exported in Go as os.Readlink . It reads the content of a symlink and does no further processing — no checking for existence or recursive processing.

target, err := os.Readlink("test1")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM