Como realizar a verificação do sistema de arquivos

104
  1. Eu preciso escrever uma função que, quando fornecido o caminho de uma pasta, verifica os arquivos enraizados nessa pasta.
  2. E então preciso exibir a estrutura de diretório dessa pasta.

Eu sei fazer 2 (vou usar o jstree para exibi-lo no navegador).

chinmay
fonte
2
você precisa passar pela árvore de diretórios recursivamente?
newacct

Respostas:

194

EDIT : Muitas pessoas ainda acertaram esta resposta, que pensei em atualizá-lo para a API Go1. Este é um exemplo funcional de filepath.Walk () . O original está abaixo.

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

Observe que filepath.Walk percorre a árvore de diretórios recursivamente.

Este é um exemplo de execução:

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

SEGUE RESPOSTA ORIGINAL: A interface para percorrer caminhos de arquivo mudou desde a semana de 2011-2011-09-16, consulte http://groups.google.com/group/golang-nuts/msg/e304dd9cf196a218 . O código abaixo não funcionará para versões de lançamento do GO em um futuro próximo.

Na verdade, há uma função na biblioteca padrão apenas para isso: 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
fonte
1
filepath.Walknão segue links simbólicos pelo caminho.
0xcaff
3
O filepath.Walkretorno de chamada @FrancescoPasa será acionado em links simbólicos (arquivo e diretório). Sim, ele não os seguirá , mas o retorno de chamada reconhece um link simbólico e executa outras ações, ou seja, um acompanhamento filepath.Walkgarantindo primeiro que o caminho ainda não foi visitado.
colm.anseo
15

Esta é uma maneira de obter informações sobre os arquivos de um diretório.

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
fonte
@peterSO: o que Readdir (-1) significa? como o Readdir só aceita o tipo de string, e com base na documentação da API, uma string não pode ser NUL, e nenhuma outra limitação .. e qual é o tipo de retorno do "fi" no Readdir como é que pode ser percorrido (é um mapa?) ..
sateayam
@heike: Veja minha resposta revisada, que agora inclui a documentação da API. Como você pode ver, o Readdirparâmetro do método é num int. Se n <= 0, Readdirretorna todos os FileInfodo diretório em uma única fatia.
peterSO
@RickSmith: Veja o pacote os func (FileMode) IsRegular.
peterSO
1
não seja exigente, mas seu adiamento de fechamento deve ocorrer antes da verificação de erro.
Zanven de
13

Aqui está um exemplo para percorrer todos os arquivos e diretórios recursivamente. Observe que se você quiser saber se o caminho que está acrescentando é um diretório, basta marcar "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)
    }
}
François
fonte
Você copiou e colou uma função? O mainmétodo não deve ter ([]string, error)args e você precisa fazer algo com ele err. A menos que na hora da resposta fosse válido? Definitivamente, um erro de compilação em versões mais recentes. Caso contrário, muito útil, obrigado.
Steve
7

O pacote github.com/kr/fsfornece uma WalkerAPI muito interessante.

Mostafa
fonte
4

O pacote padrão Go ioutiltem uma função incorporada para este cenário de caso, veja o exemplo abaixo

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

Observe que "Walk não segue links simbólicos", portanto, se você deseja escrever uma função que faça isso, recomendo ioutil.ReadDir . Meu próprio teste de benchmark mostrou que é mais rápido e consome menos memória do que filepath.Glob .

Além disso, ioutil.ReadDirestá classificando os arquivos por nome de base usando comparação de strings básicas ( strA > strB). Como um cara de devops, geralmente classifico nomes de dir fazendo uma comparação numérica reversa (última compilação primeiro, por exemplo). Se esse também for o seu caso, é melhor chamar os.ReadDir diretamente ( ioutil.ReadDirestá chamando isso nos bastidores ) e fazer a classificação você mesmo.

Aqui está um exemplo da ReadDirparte com classificação 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
fonte
0

Você pode querer fazer o currying de funções aqui, para que possa utilizar totalmente a pesquisa

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
fonte