GoLang string comparison with Slices - go

I'm able to get a list of files and folders from a directory, I've written a function called isDir to return True if the path is a directory.
Now my problem is that I want to make sure that none of the folders listed match a list of strings in a slice. The code I have might skip the first match but it will print out everything else anyways. I need to process the folders that aren't to be avoided.
Code is for Windows 7/8 directories but I should be able to get a Linux sample working too should one be provided.
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func isDir(pth string) (bool) {
fi, err := os.Stat(pth)
if err != nil {
return false
}
return fi.Mode().IsDir()
}
func main() {
gcomputer := "localhost"
location := fmt.Sprintf("\\\\%s\\c$\\Users\\", gcomputer)
// Profiles to avoid
avoid := []string{"Administrator", "Default", "Public"}
// walk through files & folders in the directory (location) & return valid profiles
files, _ := ioutil.ReadDir(location)
for _, f := range files {
fdir := []string{location, f.Name()}
dpath := strings.Join(fdir, "")
if isDir(dpath) {
for _, iavoid := range avoid {
for iavoid != f.Name() {
fmt.Println(dpath)
break
}
break
}
}
}
}
I don't mind using a third-party module, I've been working on this too long and starting to lose my cool, making understanding the docs a bit difficult. Any tips would be appreciated. Thank you.

If your list of strings to avoid gets bigger, you might not want to be iterating over them for every directory. Might I suggest a small modification to #peterSO's answer:
package main
import (
"fmt"
"io/ioutil"
)
var avoidanceSet = map[string]bool{
"Administrator": true,
"Default": true,
"Public": true,
}
func avoid(name string) bool {
_, inSet := avoidanceSet[name]
return inSet
}
func main() {
gcomputer := "localhost"
location := fmt.Sprintf("\\\\%s\\c$\\Users\\", gcomputer)
files, err := ioutil.ReadDir(location)
if err != nil {
fmt.Println(err)
return
}
for _, f := range files {
if f.IsDir() && !avoid(f.Name()) {
dpath := location + f.Name()
fmt.Println(dpath)
}
}
}

For example,
package main
import (
"fmt"
"io/ioutil"
)
func avoid(name string) bool {
profiles := []string{"Administrator", "Default", "Public"}
for _, p := range profiles {
if name == p {
return true
}
}
return false
}
func main() {
gcomputer := "localhost"
location := fmt.Sprintf("\\\\%s\\c$\\Users\\", gcomputer)
files, err := ioutil.ReadDir(location)
if err != nil {
fmt.Println(err)
return
}
for _, f := range files {
if f.IsDir() && !avoid(f.Name()) {
dpath := location + f.Name()
fmt.Println(dpath)
}
}
}

Related

Copy files into a folder until it reaches a certain size

I have a folder named "myfolder" that has some txt and jpeg files and and a folder named "test".
I want to copy files inside myfolder to test until it reaches a certain size, for example 10MB, then stop to copy.
This is my code that does not work:
package main
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
)
var (
MyFolderPath = "/home/jim/myfolder"
files []string
)
func copy(source []string, destination string) {
for a, b := range source {
input, _ := ioutil.ReadFile(b)
ioutil.WriteFile(destination+strconv.Itoa(a), input, 0777)
}
}
func CopyFile(path string) {
var size int64
done := errors.New("size reached")
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
if err != nil {
return err
}
copy(files[1:], fmt.Sprintf("%v/test/%v", MyFolderPath, "file"))
size += info.Size()
if size > 10000 {
return done
}
return err
})
if err != nil {
fmt.Println(err)
}
}
func CollectFiles() {
err := filepath.Walk(MyFolderPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Println(err)
}
files = append(files, path)
return nil
})
if err != nil {
fmt.Println(err)
}
}
func main() {
CollectFiles()
CopyFile(fmt.Sprintf("%v/test", MyFolderPath))
}
What I do wrong and how can I do in right way?
This line is wrong:
copy(files[1:], fmt.Sprintf("%v/test/%v", MyFolderPath, "file"))
Every time you find a file match, you tell it to (re)-copy every file in your files slice, except the first one.
You probably want to copy a single file there.
Although your code is difficult to reason through--I'm not sure why files exists at all, nor why you're walking your directory structure twice. So your goals aren't really clear, so it's hard to be more specific.
Also note: You should not use copy as the name of a function, as that is a built-in, so makes your code very confusing to read.
You have multiple places where you're wrong. Perhaps check this code and compare:
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
)
var (
MyFolderPath = "/tmp"
DestPath = "/tmp/dest"
CumulativeSize int64 = 0 // in bytes
ErrDone = errors.New("done")
)
const UpperLimit = 1024 * // 1 Kilobyte
1024 * // 1 Megabyte
10 // 10 Megabytes
func main() {
err := filepath.Walk(MyFolderPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("problem with file (%s): %v", path, err)
return nil
}
if info.IsDir() {
return filepath.SkipDir // skip it, walk will enter it too
}
CumulativeSize += info.Size()
if CumulativeSize >= UpperLimit {
return ErrDone
}
// copy file here
return nil
})
if err == ErrDone {
fmt.Println("finished successfully")
} else {
fmt.Printf("error: %v", err)
}
}

List all functions from source code

I have folder with .go files and functions defined inside them.
Is it possible to list in command line all function declarations in current folder, probably with godoc?
godoc list functions /path/to/fileOrFolder
To have such output:
func Foo(a, b int) int
func Bar(c, d int) int
Definitely Peter's answer is very much sufficient, but if you want to go down the rabbit hole... and for the fun of it. Using powers of golang std lib ast.
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"log"
"os"
)
func main() {
// read file
// here you can filepath.Walk() for your go files
gopath := os.ExpandEnv("$GOPATH")
fname := gopath + "/src/github.com/golang/protobuf/proto/lib.go"
// read file
file, err := os.Open(fname)
if err != nil {
log.Println(err)
return
}
defer file.Close()
// read the whole file in
srcbuf, err := ioutil.ReadAll(file)
if err != nil {
log.Println(err)
return
}
src := string(srcbuf)
// file set
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "lib.go", src, 0)
if err != nil {
log.Println(err)
return
}
// main inspection
ast.Inspect(f, func(n ast.Node) bool {
switch fn := n.(type) {
// catching all function declarations
// other intersting things to catch FuncLit and FuncType
case *ast.FuncDecl:
fmt.Print("func ")
// if a method, explore and print receiver
if fn.Recv != nil {
fmt.Printf("(%s)", fields(*fn.Recv))
}
// print actual function name
fmt.Printf("%v", fn.Name)
// print function parameters
if fn.Type.Params != nil {
fmt.Printf("(%s)", fields(*fn.Type.Params))
}
// print return params
if fn.Type.Results != nil {
fmt.Printf("(%s)", fields(*fn.Type.Results))
}
fmt.Println()
}
return true
})
}
func expr(e ast.Expr) (ret string) {
switch x := e.(type) {
case *ast.StarExpr:
return fmt.Sprintf("%s*%v", ret, x.X)
case *ast.Ident:
return fmt.Sprintf("%s%v", ret, x.Name)
case *ast.ArrayType:
if x.Len != nil {
log.Println("OH OH looks like homework")
return "TODO: HOMEWORK"
}
res := expr(x.Elt)
return fmt.Sprintf("%s[]%v", ret, res)
case *ast.MapType:
return fmt.Sprintf("map[%s]%s", expr(x.Key), expr(x.Value))
case *ast.SelectorExpr:
return fmt.Sprintf("%s.%s", expr(x.X), expr(x.Sel))
default:
fmt.Printf("\nTODO HOMEWORK: %#v\n", x)
}
return
}
func fields(fl ast.FieldList) (ret string) {
pcomma := ""
for i, f := range fl.List {
// get all the names if present
var names string
ncomma := ""
for j, n := range f.Names {
if j > 0 {
ncomma = ", "
}
names = fmt.Sprintf("%s%s%s ", names, ncomma, n)
}
if i > 0 {
pcomma = ", "
}
ret = fmt.Sprintf("%s%s%s%s", ret, pcomma, names, expr(f.Type))
}
return ret
}
Building on #Peter's idea on an approach, you extract out all the exported functions using a simple grep + regex like this:
grep -rP '^func\s(?:\([^\)]+\)\s)?[A-Z].*' *.go

simple static server who return static files in rotation for every request

i have this simple server writed in golang :
package main
import (
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.Dir("./static")))
http.ListenAndServe(":3000", nil)
}
I want add new function :
every request GET /rotate return one file content in rotation from /static folder.
for example in /static folder exist 7 file, for every request server return : file1, file2, file3 ...
How i can do this in go ?
Below is a simple server that should get you started toward your goal. A couple of things to note about the implementation:
The file names are loaded only once during program startup. If a file disappears while the program is running, the server will return a 404 when it comes up in the rotation.
We need to use locking (in this case, via sync.Mutex) as each request will run in its own go routine.
package main
import (
"flag"
"net/http"
"os"
"path/filepath"
"sync"
)
func main() {
dir := flag.String("dir", ".", "directory of files to serve")
flag.Parse()
f, err := os.Open(*dir)
if err != nil {
panic(err)
}
files, err := f.Readdir(0)
if err != nil {
panic(err)
}
filenames := make([]string, 0, len(files))
for _, file := range files {
if !file.IsDir() {
filenames = append(filenames, file.Name())
}
}
var (
idxLock sync.Mutex
idx int
)
http.HandleFunc("/rotate", func(w http.ResponseWriter, r *http.Request) {
if len(filenames) == 0 {
http.NotFound(w, r)
return
}
idxLock.Lock()
i := idx
idx++
if idx >= len(filenames) {
idx = 0
}
idxLock.Unlock()
http.ServeFile(w, r, filepath.Join(*dir, filenames[i]))
})
if err := http.ListenAndServe(":3000", nil); err != nil {
panic(err)
}
}

Golang find most recent file by date and time

I am not sure if I am doing this correctly, but ultimately I would like to find the most recent modified date of a file in a directory and return the file name. The code I have so far is as follows. Can someone please help me with a more efficient solution than this. I really have a feeling this is super hacky. What I am doing is getting the dates and removing the
package main
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
)
func main() {
dir := "C:\\temp\\"
files, _ := ioutil.ReadDir(dir)
for _, f := range files {
fi, _ := os.Stat(dir + f.Name())
s := strings.Split(fi.ModTime().Format("2006-01-02 15.04.05.000"), " ")
fdate, err := strconv.Atoi(strings.Replace(s[0], "-", "", -1))
if err != nil {
fmt.Println(err)
}
ftime, err := strconv.Atoi(strings.Replace(s[1], ".", "", -1))
if err != nil {
fmt.Println(err)
}
fmt.Println(fi.Name(), fdate+ftime)
}
}
Pay attention to details and efficiency. Check for errors. You asked for files so skip directories and other things. Allow for multiple files with the same modified time stamp (for example, Windows file times have a resolution of, at best, 100-nanoseconds). You already have ModTime() so don't call os.Stat(). Use time.Time methods directly. And so on.
For example,
package main
import (
"fmt"
"io/ioutil"
"os"
"time"
)
func main() {
dir := `C:\temp\` // Windows directory
files, err := ioutil.ReadDir(dir)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
var modTime time.Time
var names []string
for _, fi := range files {
if fi.Mode().IsRegular() {
if !fi.ModTime().Before(modTime) {
if fi.ModTime().After(modTime) {
modTime = fi.ModTime()
names = names[:0]
}
names = append(names, fi.Name())
}
}
}
if len(names) > 0 {
fmt.Println(modTime, names)
}
}
You can just compare the outputs of fi.ModTime().Unix() and keep the largest value to find the most recently modified file.
For example:
package main
import (
"fmt"
"io/ioutil"
"os"
)
func main() {
dir := "/tmp/"
files, _ := ioutil.ReadDir(dir)
var newestFile string
var newestTime int64 = 0
for _, f := range files {
fi, err := os.Stat(dir + f.Name())
if err != nil {
fmt.Println(err)
}
currTime := fi.ModTime().Unix()
if currTime > newestTime {
newestTime = currTime
newestFile = f.Name()
}
}
fmt.Println(newestFile)
}

Determine whether dynamically specified Go variable has specific type

I'm writing a command line application where a user specifies 1) a directory containing Go files, 2) the name of a variable that should be a http.Handler, e.g.
go run cli.go /path/to/a/go/library MyCustomHandler
I'm trying to
parse the files
find the variable with the given name
verify that it's a http.Handler
I can do the first two no problem - I call parser.ParseDir, then get the package I want as an *ast.Package, then loop over it like this:
func findHttpHandler(pkg *ast.Package, handlerName string) (*ast.FuncDecl, error) {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
gd, ok := decl.(*ast.GenDecl)
if !ok || gd.Tok != token.VAR {
continue
}
if len(gd.Specs) != 1 {
continue
}
spec0 := gd.Specs[0]
vs, ok := spec0.(*ast.ValueSpec)
if !ok {
continue
}
if len(vs.Names) != 1 {
continue
}
ident := vs.Names[0]
if ident.Name != handlerName {
continue
}
// ...
}
}
}
The problem is at this point the ValueSpec.Type is nil, and there doesn't appear to be any way to figure out whether this is a http.Handler or not.
The go/types package has more tools for checking types, but it also looks like you need to do a lot more setup work to get this, essentially parsing and type checking the whole program. Am I going to need to go down this path, or is there an easier way, just using the ast package, or using go build somehow?
did some trace and find the way, hoping help
https://play.golang.org/p/f4XF8K_FbL
package main
import (
"go/parser"
"go/token"
"os"
"go/ast"
"log"
"net/http"
//"reflect"
)
func MyCustomHandler(w http.ResponseWriter, r* http.Request){
}
func findHttpHandler(pkg *ast.Package, handlerName string) (*ast.FuncDecl, error) {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
fd, ok := decl.(*ast.FuncDecl)
if !ok || fd == nil{
continue
}
if fd.Name.Name != handlerName{
continue
}
if len(fd.Type.Params.List) == 2 {
p1 := fd.Type.Params.List[0]
p2 := fd.Type.Params.List[1]
exp, ok := p1.Type.(*ast.SelectorExpr)
if !ok{
break;
}
ident, ok := exp.X.(*ast.Ident)
if !ok{
break
}
if ident.Name!="http" || exp.Sel.Name != "ResponseWriter"{
break;
}
exp2, ok := p2.Type.(*ast.StarExpr)
if !ok{
break;
}
exp = exp2.X.(*ast.SelectorExpr)
ident, ok = exp.X.(*ast.Ident)
if !ok{
break
}
if ident.Name!="http" || exp.Sel.Name != "Request"{
break;
}
return fd, nil
}
}
}
return nil, nil
}
func main() {
fs := token.NewFileSet()
pkgs, err := parser.ParseDir(fs, os.Args[1], nil, parser.Trace)
if err != nil{
log.Fatalln(err)
}
for _,pkg:=range pkgs{
d, _ := findHttpHandler(pkg, "MyCustomHandler");
log.Println(d)
}
}

Resources