Find binary's in $PATH - go

I am looking for a function to find a binary in all folders which are available in $PATH.
I know i can use os.Getenv("PATH") but it returns:
path: /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/usr/local/go/bin
Which needs to be processed.
Ideally function like this: FindBinary("ntpq") and it will return the path + binary name and false when not found in path.
Does anybody have a ready function?

You can use the LookPath function from the os/exec package:
path, err := exec.LookPath("fortune")
if err != nil {
log.Fatal("installing fortune is in your future")
}
fmt.Printf("fortune is available at %s\n", path)

Related

Golang os.Create path with nested directories

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.

How to change timestamp for symbol link using golang?

os.Chtimes always to follow symlinks and change the real files timestamp.
Is there a method to change the symlinks timestamp in?
Just like touch -h does.
Not sure it's possible, at least from the syscall package.
Looking at the source-code for say syscall.Chtimes:
func Chtimes(name string, atime time.Time, mtime time.Time) error {
var utimes [2]syscall.Timespec
utimes[0] = syscall.NsecToTimespec(atime.UnixNano())
utimes[1] = syscall.NsecToTimespec(mtime.UnixNano())
if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
return &PathError{"chtimes", name, e}
}
return nil
}
duplicating this code - and removing the fixLongPath call which I assumed followed the symlinks - still affects the target file, not the source symlink.
Even trying this operation on a symlink which points to a non-existent file, returns a runtime error no such file or directory.
A CGO pkg - could, but that seems overkill.
If you use linux, you can use golang.org/x/sys/unix package, which provides Lutimes
import "golang.org/x/sys/unix"
unix.Lutimes(symlink, nil)
You can check if a symlink exists and if so, remove it and create another one.
if _, err := os.Lstat(symlinkPath); err == nil {
os.Remove(symlinkPath)
}
err := os.Symlink(filePath, symlinkPath)
if err != nil {
fmt.Println(err)
}

Get the parent path

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

Go get parent directory

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.

check if given path is a subdirectory of another in golang

Say we have two paths:
c:\foo\bar\baz and c:\foo\bar
Is there any package/method that will help me determine if one is a subdirectory of another? I am looking at a cross-platform option.
You could try and use path.filepath.Rel():
func Rel(basepath, targpath string) (string, error)
Rel returns a relative path that is lexically equivalent to targpath when joined to basepath with an intervening separator.
That is, Join(basepath, Rel(basepath, targpath)) is equivalent to targpath itself
That means Rel("c:\foo\bar", "c:\foo\bar\baz") should be baz, meaning a subpath completely included in c:\foo\bar\baz, and without any '../'.
The same would apply for unix paths.
That would make c:\foo\bar\baz a subdirectory of c:\foo\bar.
I haven't found a reliable solution for all types of paths, but the best you can get is by using filepath.Rel as VonC suggested.
It works if both filepaths are either absolute or relative (mixing is not allowed) and works on both Windows and Linux:
func SubElem(parent, sub string) (bool, error) {
up := ".." + string(os.PathSeparator)
// path-comparisons using filepath.Abs don't work reliably according to docs (no unique representation).
rel, err := filepath.Rel(parent, sub)
if err != nil {
return false, err
}
if !strings.HasPrefix(rel, up) && rel != ".." {
return true, nil
}
return false, nil
}
Absolute windows paths that start with a drive letter will require an additional check though.
You can use the function path.filepath.Match()
Match reports whether name matches the shell file name pattern.
For example:
pattern := "C:\foo\bar" + string(filepath.Separator) + "*"
matched, err := filepath.Match(pattern, "C:\foo\bar\baz")
Where matched should be true.
If you first canonicalize both paths by calling filepath.EvalSymlinks() and filepath.Abs() on them, you can simply append a '/' to each one, since the UNIX kernel itself forbids a '/' within a path component. At this point you can simply use strings.HasPrefix() on the two paths, in either order.
Try this code. This checks if either is a sub-directory of the other. Try changing values of both base and path and the results should be valid.
package main
import (
"fmt"
"path/filepath"
"strings"
)
func main() {
base := "/b/c/"
path := "/a/b/c/d"
if len(base) > len(path) {
base, path = path, base
}
rel, err := filepath.Rel(base, path)
fmt.Printf("Base %q: Path %q: Rel %q Err %v\n", base, path, rel, err)
if err != nil {
fmt.Println("PROCEED")
return
}
if strings.Contains(rel, "..") {
fmt.Println("PROCEED")
return
}
fmt.Println("DENY")
}

Resources