How to read file/path containing tilde - go

The following code gives ENOENT (2) Do you know how to get stat of a file containing tilde?
file := "~/.zshrc"
fileStat, err := os.Stat(file)
if err != nil {
return 0, err
}

The tilde is not something that the file system calls are able to interpret - it has a meaning only in shells like bash, which typically interprets it as $HOME. So you'll probably want to use os.Getenv("HOME") and then replace ~ with the result. Or, as suggested by Allon Guralnek in a comment, use os.UserHomeDir(), which reads the appropriate environment variable depending on your OS.

You can access the home directory of the current user using the os/user package.
Something like this will get you near what you want:
package main
import (
"fmt"
"log"
"os"
"os/user"
)
func main() {
usr, err := user.Current()
if err != nil {
log.Fatal(err)
}
fmt.Println(usr.HomeDir)
file := usr.HomeDir + "/.zshrc"
fileStat, err := os.Stat(file)
if err != nil {
log.Fatal(err)
}
fmt.Println(fileStat)
}

Related

Capturing the output of exec.Command("find", "./helloworld/workspace", "-name", "*.java").Output()

I'm trying to find the path of the helloworld.java file so that I can pass it down to a compiler function.
What I have:
I'm expecting this to return the path, of type []byte and then stringify it, of the only helloworld.java file in this directory and then pass it down to Java() function.
filePath, _ := exec.Command("find", "./helloworld/workspace", "-name", "*.java").Output()
Java(string(filePath))
The Problem is that cmd := exec.Command("javac", filePath) in my java() function is not recognizing the file path therefore not compiling it.
But if I hardcode the path that I get from exec.Command("find) like this:
This works fine
cmd := exec.Command("javac", "./helloworld/workspace/src/main/java/com/coveros/demo/helloworld/HelloWorld.java")
err := cmd.Run()
But this does not work:
What am I missing, How do I fix this?
func Java(filePath string) {
fmt.Println("compiler start")
cmd := exec.Command("javac", filePath)
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
fmt.Println("compiler End")
}
I think the result from find is returning multiple possible paths which are separated by a newline "\n". The newline will be "hidden" if it is printed to the command line. You can try this fmt.Println(stringPath + "hello, am I on a new line?") to show the stringPath has a new line in it.
See the below which uses a similar version of find looking for json files, then splits the string by newlines and then loops through these paths. If the path is blank (which it can be) it skips over it.
package main
import (
"fmt"
"os/exec"
"strings"
)
func main() {
filePath, err := exec.Command("find", ".", "-name", "*.json").Output()
if err != nil {
panic(err)
}
stringPath := string(filePath)
paths := strings.Split(stringPath, "\n")
CatFile(paths)
}
func CatFile(filePaths []string) {
for _, path := range filePaths {
if len(path) == 0 {
continue
}
output, err := exec.Command("cat", path).Output()
if err != nil {
fmt.Println("Error!")
fmt.Println(err)
}
fmt.Println(string(output))
}
}
See this related question, which discusses this: Go lang differentiate "\n" and line break

How to get root folder of project, two level up

Im inside deep folder inside file and I want to get the dir which is 2 level up, I see the following api but not sure how to add something like
"../../" , any idea?
dir, err := os.Getwd()
for example I get the following
/Users/i02222333/go/src/myapp/src/test
And I need
/Users/i02222333/go/src/myapp
I can cut the string with the path with some manipulation but my question is if there is better solution to do it with golang ?
You can get the directory name of '../../' using the path package like this:
package main
import (
"fmt"
"os"
"path"
)
func main() {
dirname, err := os.Getwd()
if err != nil {
panic(err)
}
fmt.Printf("Current directory: %v\n", dirname)
dir, err := os.Open(path.Join(dirname, "../../"))
if err != nil {
panic(err)
}
fmt.Printf("Name of ../../: %v\n", dir.Name())
}
Here is an example of my output:
$ go run main.go
Current directory: /Users/jack/go/src/stackoverflow/example/directory
Name of ../../: /Users/jack/go/src/stackoverflow

Read from console while piping commands in Go

I need a way to read input from console twice (1 -from cat outupt, 2 - user inputs password), like this:
cat ./test_data/test.txt | app account import
My current code skips password input:
reader := bufio.NewReader(os.Stdin)
raw, err := ioutil.ReadAll(reader)
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
wlt, err := wallet.Deserialize(string(raw))
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
fmt.Print("Enter password: ")
pass := ""
fmt.Fscanln(reader, &pass)
Also tried to read password with Scanln - doesn't works.
Note:
cat (and piping at all) can't be used with user input, as shell redirects inputs totally.
So the most simple solutions are:
to pass filename as argument
redirect manually app account import < ./test.txt
Read file and password separately (and don't show password), try this:
package main
import (
"fmt"
"io/ioutil"
"log"
"github.com/howeyc/gopass"
)
func main() {
b, err := ioutil.ReadFile("./test_data/test.txt") // just pass the file name
if err != nil {
fmt.Print(err)
}
fmt.Println(string(b))
fmt.Print("Password: ")
pass, err := gopass.GetPasswd()
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(pass))
}
and go get github.com/howeyc/gopass first.

Run Multiple Exec Commands in the same shell golang

I'm having trouble figuring out how to run multiple commands using the os/exec package. I've trolled the net and stackoverflow and haven't found anything that works for me case. Here's my source:
package main
import (
_ "bufio"
_ "bytes"
_ "errors"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
)
func main() {
ffmpegFolderName := "ffmpeg-2.8.4"
path, err := filepath.Abs("")
if err != nil {
fmt.Println("Error locating absulte file paths")
os.Exit(1)
}
folderPath := filepath.Join(path, ffmpegFolderName)
_, err2 := folderExists(folderPath)
if err2 != nil {
fmt.Println("The folder: %s either does not exist or is not in the same directory as make.go", folderPath)
os.Exit(1)
}
cd := exec.Command("cd", folderPath)
config := exec.Command("./configure", "--disable-yasm")
build := exec.Command("make")
cd_err := cd.Start()
if cd_err != nil {
log.Fatal(cd_err)
}
log.Printf("Waiting for command to finish...")
cd_err = cd.Wait()
log.Printf("Command finished with error: %v", cd_err)
start_err := config.Start()
if start_err != nil {
log.Fatal(start_err)
}
log.Printf("Waiting for command to finish...")
start_err = config.Wait()
log.Printf("Command finished with error: %v", start_err)
build_err := build.Start()
if build_err != nil {
log.Fatal(build_err)
}
log.Printf("Waiting for command to finish...")
build_err = build.Wait()
log.Printf("Command finished with error: %v", build_err)
}
func folderExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return true, err
}
I want to the command like I would from terminal. cd path; ./configure; make
So I need run each command in order and wait for the last command to finish before moving on. With my current version of the code it currently says that ./configure: no such file or directory I assume that is because cd path executes and in a new shell ./configure executes, instead of being in the same directory from the previous command. Any ideas?
UPDATE I solved the issue by changing the working directory and then executing the ./configure and make command
err = os.Chdir(folderPath)
if err != nil {
fmt.Println("File Path Could not be changed")
os.Exit(1)
}
Still now i'm curious to know if there is a way to execute commands in the same shell.
If you want to run multiple commands within a single shell instance, you will need to invoke the shell with something like this:
cmd := exec.Command("/bin/sh", "-c", "command1; command2; command3; ...")
err := cmd.Run()
This will get the shell to interpret the given commands. It will also let you execute shell builtins like cd. Note that this can be non-trivial to substitute in user data to these commands in a safe way.
If instead you just want to run a command in a particular directory, you can do that without the shell. You can set the current working directory to execute the command like so:
config := exec.Command("./configure", "--disable-yasm")
config.Dir = folderPath
build := exec.Command("make")
build.Dir = folderPath
... and continue on like you were before.

How to create nested directories using Mkdir in Golang?

I am trying to create a set of nested directories from a Go executable such as 'dir1/dir2/dir3'. I have succeeded in creating a single directory with this line:
os.Mkdir("." + string(filepath.Separator) + c.Args().First(),0777);
However, I have no idea how to approach creating a predetermined nested set of directories inside of that directory.
os.Mkdir is used to create a single directory. To create a folder path, instead try using:
os.MkdirAll(folderPath, os.ModePerm)
Go documentation
func MkdirAll(path string, perm FileMode) error
MkdirAll creates a directory named path, along with any necessary parents, and returns nil, or else returns an error. The permission bits perm are used for all directories that MkdirAll creates. If path is already a directory, MkdirAll does nothing and returns nil.
Edit:
Updated to correctly use os.ModePerm instead.
For concatenation of file paths, use package path/filepath as described in #Chris' answer.
This way you don't have to use any magic numbers:
os.MkdirAll(newPath, os.ModePerm)
Also, rather than using + to create paths, you can use:
import "path/filepath"
path := filepath.Join(someRootPath, someSubPath)
The above uses the correct separators automatically on each platform for you.
If the issue is to create all the necessary parent directories, you could consider using os.MkDirAll()
MkdirAll creates a directory named path, along with any necessary parents, and returns nil, or else returns an error.
The path_test.go is a good illustration on how to use it:
func TestMkdirAll(t *testing.T) {
tmpDir := TempDir()
path := tmpDir + "/_TestMkdirAll_/dir/./dir2"
err := MkdirAll(path, 0777)
if err != nil {
t.Fatalf("MkdirAll %q: %s", path, err)
}
defer RemoveAll(tmpDir + "/_TestMkdirAll_")
...
}
(Make sure to specify a sensible permission value, as mentioned in this answer)
This is one alternative for achieving the same but it avoids race condition caused by having two distinct "check ..and.. create" operations.
package main
import (
"fmt"
"os"
)
func main() {
if err := ensureDir("/test-dir"); err != nil {
fmt.Println("Directory creation failed with error: " + err.Error())
os.Exit(1)
}
// Proceed forward
}
func ensureDir(dirName string) error {
err := os.MkdirAll(dirName, os.ModeDir)
if err == nil || os.IsExist(err) {
return nil
} else {
return err
}
}
An utility method like the following can be used to solve this.
import (
"os"
"path/filepath"
"log"
)
func ensureDir(fileName string) {
dirName := filepath.Dir(fileName)
if _, serr := os.Stat(dirName); serr != nil {
merr := os.MkdirAll(dirName, os.ModePerm)
if merr != nil {
panic(merr)
}
}
}
func main() {
_, cerr := os.Create("a/b/c/d.txt")
if cerr != nil {
log.Fatal("error creating a/b/c", cerr)
}
log.Println("created file in a sub-directory.")
}

Resources