Как выполнить сканирование файловой системы

104
  1. Мне нужно написать функцию, которая при задании пути к папке сканирует файлы с корнем в этой папке.
  2. Затем мне нужно отобразить структуру каталогов в этой папке.

Я знаю, как сделать 2 (я собираюсь использовать jstree, чтобы отобразить его в браузере).

Чинмей
источник
2
вам нужно, чтобы он рекурсивно проходил по дереву каталогов?
newacct

Ответы:

194

РЕДАКТИРОВАТЬ : достаточное количество людей все еще находят этот ответ, и я подумал, что обновлю его для API Go1. Это рабочий пример filepath.Walk () . Оригинал ниже.

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

Обратите внимание, что filepath.Walk рекурсивно просматривает дерево каталогов.

Это пример запуска:

$ 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>

ОРИГИНАЛЬНЫЙ ОТВЕТ СЛЕДУЮЩИЙ: Интерфейс для путей ходьбы файлов изменились по состоянию на weekly.2011-09-16 см http://groups.google.com/group/golang-nuts/msg/e304dd9cf196a218 . Приведенный ниже код не будет работать для релизных версий GO в ближайшем будущем.

На самом деле для этого в стандартной библиотеке есть функция: 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)
}
Лазлох
источник
1
filepath.Walkкстати, не следует символическим ссылкам.
0xcaff
3
Обратный filepath.Walkвызов @FrancescoPasa будет запускаться для символических ссылок (как файлов, так и каталога). Да, он не будет следовать за ними, но обратный вызов распознает символическую ссылку и предпримет дальнейшие действия, например, последующие действия, filepath.Walkгарантирующие, что путь еще не был посещен.
colm.anseo 08
15

Вот способ получить информацию о файлах в каталоге.

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: что означает Readdir (-1)? поскольку Readdir принимает только строковый тип, и, согласно документации API, строка не может быть только NUL, и никаких других ограничений ... и каков тип возвращаемого значения "fi" в Readdir, как это можно пройти (это карта?) ..
sateayam 09
@heike: См. мой исправленный ответ, который теперь включает документацию по API. Как видите, Readdirпараметр метода - nэто файл int. Если n <= 0, Readdirвозвращает все FileInfoиз каталога в одном фрагменте.
peterSO
@RickSmith: см. Пакет os func (FileMode) IsRegular.
peterSO
1
не быть разборчивым, но ваше отсроченное закрытие должно произойти до проверки ошибок.
Занвен
13

Вот пример рекурсивного перебора всех файлов и каталогов. Обратите внимание: если вы хотите знать, является ли добавляемый путь каталогом, просто отметьте «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)
    }
}
Франсуа
источник
Вы скопировали и вставили функцию? У mainметода не должно быть ([]string, error)аргументов, и вам нужно что-то делать с err. Разве на момент ответа он был действителен? Определенно ошибка компиляции в более поздних версиях. В остальном очень полезно, спасибо.
Стив
4

Стандартный пакет Go ioutilимеет встроенную функцию для этого сценария, см. Пример ниже

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())
    }
}
Джимми Обоньо Абор
источник
1

Обратите внимание, что «Walk не следует по символическим ссылкам», поэтому, если вы хотите написать функцию, которая делает это, я рекомендую ioutil.ReadDir . Мой собственный тест показал, что он быстрее и потребляет меньше памяти, чем filepath.Glob .

Кроме того, ioutil.ReadDirфайлы сортируются по базовому имени с помощью простого сравнения строк ( strA > strB). Как парень DevOps, я обычно сортирую имена каталогов, выполняя обратное числовое сравнение (например, сначала последняя сборка). Если это также ваш случай, то лучше вызвать os.ReadDir напрямую ( ioutil.ReadDirвызывает это из-под обложек) и выполнить сортировку самостоятельно.

Вот пример ReadDirдетали с числовой сортировкой:

// 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
источник
0

Возможно, вы захотите выполнить каррирование функций здесь, чтобы вы могли полностью использовать поиск

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
источник