Як виконати сканування файлової системи


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>

ОРИГІНАЛЬНІ ВІДПОВІДИ: Інтерфейс для проходження шляхів до файлів змінився станом на тиждень 2011-09-16, див. Http://groups.google.com/group/golang-nuts/msg/e304dd9cf196a218 . Код, приведений нижче, не буде працювати для версій GO в найближчому майбутньому.

Насправді функція в стандартній lib саме для цього: 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
@FrancescoPasa filepath.Walkзворотний виклик буде ініційовано на символьних посиланнях (як файл, так і каталог). Так, вони не підуть за ними, але зворотний виклик розпізнає символьне посилання та вживає подальших дій, тобто спостереження, filepath.Walkспочатку переконуючись, що шлях ще не був відвіданий.
colm.anseo

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, а жодним іншим обмеженням. (це карта?) ..
sateayam

@heike: Дивіться мою переглянуту відповідь, яка тепер включає документацію API. Як бачимо, Readdirпараметром методу є nan 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). Як хлопець девепс, я, як правило, сортую імена режисера, роблячи зворотне числове порівняння (наприклад, остання побудова спочатку). Якщо це теж ваш випадок, тоді краще зателефонувати 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
}

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
           }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.