Cómo realizar un análisis del sistema de archivos

104
  1. Necesito escribir una función que cuando se le da la ruta de una carpeta escanea los archivos enraizados en esa carpeta.
  2. Y luego necesito mostrar la estructura del directorio en esa carpeta.

Sé cómo hacer 2 (voy a usar jstree para mostrarlo en el navegador).

barbilla
fuente
2
¿Lo necesita para recorrer el árbol de directorios de forma recursiva?
newacct

Respuestas:

194

EDITAR : Suficiente gente todavía ha dado con esta respuesta, que pensé en actualizarla para la API Go1. Este es un ejemplo funcional de filepath.Walk () . El original está debajo.

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

Tenga en cuenta que filepath.Walk recorre el árbol de directorios de forma recursiva.

Esta es una ejecución de ejemplo:

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

SIGUE LA RESPUESTA ORIGINAL: La interfaz para rutas de archivos móviles ha cambiado semanalmente desde el 16 de septiembre de 2011, consulte http://groups.google.com/group/golang-nuts/msg/e304dd9cf196a218 . El siguiente código no funcionará para las versiones de lanzamiento de GO en un futuro próximo.

En realidad, hay una función en la biblioteca estándar solo para esto: 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)
}
latigazo
fuente
1
filepath.Walkno sigue enlaces simbólicos por cierto.
0xcaff
3
La filepath.Walkdevolución de llamada de @FrancescoPasa se activará en los enlaces simbólicos (tanto el archivo como el directorio). Sí, no los seguirá , pero la devolución de llamada reconocerá un enlace simbólico y tomará medidas adicionales, es decir, un seguimiento, filepath.Walkasegurándose primero de que la ruta aún no se haya visitado.
colm.anseo
15

He aquí una forma de obtener información de archivo para los archivos en un directorio.

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
fuente
@peterSO: ¿que significa Readdir (-1)? como el Readdir solo acepta el tipo de cadena, y según la documentación de la API, una cadena solo no puede ser NUL, y ninguna otra limitación ... y cuál es el tipo de retorno de la "fi" en el Readdir cómo es que se puede recorrer (¿es un mapa?) ..
sateayam
@heike: Vea mi respuesta revisada, que ahora incluye la documentación de la API. Como puede ver, el Readdirparámetro del método es nun int. Si n <= 0, Readdirdevuelve todo el FileInfodel directorio en un solo segmento.
peterSO
@RickSmith: Ver paquete os func (FileMode) IsRegular.
peterSO
1
no sea quisquilloso, pero su cierre diferido debe ocurrir antes de la verificación de errores.
Zanven
13

Aquí hay un ejemplo para recorrer todos los archivos y directorios de forma recursiva. Tenga en cuenta que si desea saber si la ruta que está agregando es un directorio, simplemente marque "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
fuente
¿Copiaste y pegaste una función? El mainmétodo no debería tener ([]string, error)argumentos y necesitas hacer algo con err. ¿A menos que al momento de contestar fuera válido? Definitivamente un error de compilación en versiones más recientes. De lo contrario, muy útil, gracias.
Steve
4

El paquete estándar Go ioutiltiene una función incorporada para este caso, vea el ejemplo a continuación

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
fuente
1

Tenga en cuenta que "Walk no sigue enlaces simbólicos", por lo que si está buscando escribir una función que lo haga, le recomiendo ioutil.ReadDir . Mi propia prueba comparativa mostró que es más rápido y requiere menos memoria que filepath.Glob .

Además, ioutil.ReadDirordena los archivos por nombre de base mediante la comparación de cadenas básica ( strA > strB). Como usuario de devops, generalmente clasifico los nombres de los directorios haciendo una comparación numérica inversa (la última compilación primero, por ejemplo). Si ese también es su caso, entonces es mejor llamar directamente a os.ReadDir ( ioutil.ReadDirestá llamando a esto bajo las sábanas) y hacer la clasificación usted mismo.

A continuación, se muestra un ejemplo de la ReadDirpieza con ordenación numérica:

// 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
fuente
0

Es posible que desee realizar funciones de búsqueda aquí, de modo que pueda utilizar completamente la búsqueda

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
fuente