So führen Sie das Scannen von Dateisystemen durch

104
  1. Ich muss eine Funktion schreiben, die, wenn der Pfad eines Ordners angegeben wird, die in diesem Ordner verwurzelten Dateien scannt.
  2. Und dann muss ich die Verzeichnisstruktur in diesem Ordner anzeigen.

Ich weiß, wie man 2 macht (ich werde jstree verwenden, um es im Browser anzuzeigen).

Chinmay
quelle
2
Benötigen Sie es, um den Verzeichnisbaum rekursiv zu durchlaufen?
Newacct

Antworten:

194

EDIT : Genug Leute haben diese Antwort noch getroffen, dass ich dachte, ich würde sie für die Go1-API aktualisieren. Dies ist ein funktionierendes Beispiel für filepath.Walk () . Das Original ist unten.

package main

import (
  "path/filepath"
  "os"
  "flag"
  "fmt"
)

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)
}

Bitte beachten Sie, dass filepath.Walk den Verzeichnisbaum rekursiv durchläuft.

Dies ist ein Beispiellauf:

$ mkdir -p dir1/dir2
$ touch dir1/file1 dir1/dir2/file2
$ go run walk.go dir1
Visited: dir1
Visited: dir1/dir2
Visited: dir1/dir2/file2
Visited: dir1/file1
filepath.Walk() returned <nil>

ORIGINAL ANTWORT FOLGT: Die Benutzeroberfläche für das Gehen von Dateipfaden hat sich seit dem wöchentlichen 2011-09-16 geändert, siehe http://groups.google.com/group/golang-nuts/msg/e304dd9cf196a218 . Der folgende Code wird in naher Zukunft nicht für Release-Versionen von GO funktionieren.

Es gibt tatsächlich eine Funktion in der Standardbibliothek, nur dafür: filepath.Walk .

package main

import (
    "path/filepath"
    "os"
    "flag"
)

type visitor int

// THIS CODE NO LONGER WORKS, PLEASE SEE ABOVE
func (v visitor) VisitDir(path string, f *os.FileInfo) bool {
    println(path)
    return true
} 

func (v visitor) VisitFile(path string, f *os.FileInfo) {
    println(path)
}

func main() {
    root := flag.Arg(0)
    filepath.Walk(root, visitor(0), nil)
}
laslowh
quelle
1
filepath.Walkfolgt übrigens keinen Symlinks.
0xcaff
3
@FrancescoPasa- filepath.WalkRückruf wird über Symlinks (sowohl Datei als auch Verzeichnis) ausgelöst. Ja, es wird ihnen nicht folgen , aber der Rückruf erkennt einen Symlink und ergreift weitere Maßnahmen, dh eine Nachverfolgung, filepath.Walkdie zunächst sicherstellt, dass der Pfad noch nicht besucht wurde.
colm.anseo
15

Hier finden Sie eine Möglichkeit, Dateiinformationen für die Dateien in einem Verzeichnis abzurufen.

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    dirname := "." + string(filepath.Separator)
    d, err := os.Open(dirname)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    defer d.Close()
    fi, err := d.Readdir(-1)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    for _, fi := range fi {
        if fi.Mode().IsRegular() {
            fmt.Println(fi.Name(), fi.Size(), "bytes")
        }
    }
}
peterSO
quelle
@peterSO: Was bedeutet Readdir (-1)? Da das Readdir nur den Zeichenfolgentyp akzeptiert und basierend auf der API-Dokumentation, kann ein String nur nicht NUL und keine andere Einschränkung sein. Und wie lautet der Rückgabetyp des "fi" im Readdir? Wie kommt es, dass es durchlaufen werden kann? (
Ist
@heike: Siehe meine überarbeitete Antwort, die jetzt die API-Dokumentation enthält. Wie Sie sehen können, ist der ReaddirMethodenparameter nein int. Wenn n <= 0, Readdirgibt alle FileInfoaus dem Verzeichnis in einem einzigen Slice zurück.
PeterSO
@ RickSmith: Siehe Paket os func (FileMode) IsRegular.
PeterSO
1
Um nicht wählerisch zu sein, sollte Ihr Aufschub vor der Fehlerprüfung erfolgen.
Zanven
13

Hier ist ein Beispiel, um alle Dateien und Verzeichnisse rekursiv zu durchlaufen. Wenn Sie wissen möchten, ob der angehängte Pfad ein Verzeichnis ist, aktivieren Sie einfach "f.IsDir ()".

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    searchDir := "c:/path/to/dir"

    fileList := []string{}
    err := filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error {
        fileList = append(fileList, path)
        return nil
    })

    for _, file := range fileList {
        fmt.Println(file)
    }
}
Francois
quelle
Haben Sie eine Funktion kopiert und eingefügt? Die mainMethode sollte keine ([]string, error)Argumente enthalten und Sie müssen etwas damit tun err. Es sei denn, zum Zeitpunkt der Beantwortung war es gültig? Auf jeden Fall ein Kompilierungsfehler in neueren Versionen. Ansonsten sehr nützlich, danke.
Steve
7

Paket github.com/kr/fsbietet Walkereine sehr interessante API.

Mostafa
quelle
4

Das Go-Standardpaket ioutilverfügt über eine integrierte Funktion für dieses Fallszenario (siehe Beispiel unten)

func searchFiles(dir string) { // dir is the parent directory you what to search
    files, err := ioutil.ReadDir(dir)
    if err != nil {
        log.Fatal(err)
    }

    for _, file := range files {
        fmt.Println(file.Name())
    }
}
Jimmy Obonyo Abor
quelle
1

Beachten Sie, dass "Walk nicht symbolischen Links folgt". Wenn Sie also eine Funktion schreiben möchten , die dies tut, empfehle ich ioutil.ReadDir . Mein eigener Benchmark-Test hat gezeigt, dass er schneller und weniger speicherintensiv ist als filepath.Glob .

Darüber hinaus werden ioutil.ReadDirDateien mithilfe des grundlegenden Zeichenfolgenvergleichs ( strA > strB) nach Basisnamen sortiert . Als Entwickler sortiere ich Dir-Namen im Allgemeinen durch einen umgekehrten numerischen Vergleich (zum Beispiel den neuesten Build zuerst). Wenn dies auch Ihr Fall ist, ist es besser, os.ReadDir direkt aufzurufen ( ioutil.ReadDirruft dies unter der Decke auf) und die Sortierung selbst durchzuführen .

Hier ist ein Beispiel für den ReadDirTeil mit numerischer Sortierung:

// ReadDirNumSort - Same as ioutil/ReadDir but uses returns a Numerically
// Sorted file list.
//
// Taken from https://golang.org/src/io/ioutil/ioutil.go
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Modified Sort method to use Numerically sorted names instead.
// It also allows reverse sorting.
func ReadDirNumSort(dirname string, reverse bool) ([]os.FileInfo, error) {
    f, err := os.Open(dirname)
    if err != nil {
        return nil, err
    }
    list, err := f.Readdir(-1)
    f.Close()
    if err != nil {
        return nil, err
    }
    if reverse {
        sort.Sort(sort.Reverse(byName(list)))
    } else {
        sort.Sort(byName(list))
    }
    return list, nil
}

// byName implements sort.Interface.
type byName []os.FileInfo

func (f byName) Len() int      { return len(f) }
func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f byName) Less(i, j int) bool {
    nai, err := strconv.Atoi(f[i].Name())
    if err != nil {
        return f[i].Name() < f[j].Name()
    }
    naj, err := strconv.Atoi(f[j].Name())
    if err != nil {
        return f[i].Name() < f[j].Name()
    }
    return nai < naj
}
DavidG
quelle
0

Möglicherweise möchten Sie hier Funktionscurrying durchführen, damit Sie die Suche vollständig nutzen können

func visit(files *[]string) filepath.WalkFunc {
    return func (path string, info os.FileInfo, err error) error {
               // maybe do this in some if block
               *files = append(*files, path)
               return nil
           }
}
Swayamraina
quelle