filepath.WalkDir în Go

Parcurge recursiv un director și apelează o funcție pentru fiecare fișier și subdirector, în ordine alfabetică.

Sintaxă
func WalkDir(root string, fn fs.WalkDirFunc) error

// fn: func(path string, d fs.DirEntry, err error) error

Ce face filepath.WalkDir

filepath.WalkDir vizitează root și tot ce se află sub el, în ordine lexicală, și apelează fn cu calea, un fs.DirEntry (nume, tip) și o eroare. Este varianta modernă și mai rapidă a lui filepath.Walk, pentru că nu citește informațiile complete (Stat) ale fiecărui fișier; dacă ai nevoie de ele, apelezi d.Info().

Valoarea returnată de fn controlează parcurgerea: nil = continuă; filepath.SkipDir = sare peste directorul curent (sau peste restul directorului părinte, dacă intrarea e un fișier); filepath.SkipAll (Go 1.20) = oprește tot, fără eroare; orice altă eroare oprește parcurgerea și este returnată de WalkDir.

Dacă un director nu poate fi citit, fn este apelată cu err != nil; tu decizi dacă ignori sau oprești. Legăturile simbolice nu sunt urmate.

Parametri

Parametru Descriere
root Directorul de pornire.
fn Funcția apelată pentru fiecare intrare.

Ce returnează

nil sau eroarea returnată de fn.

Exemple

Exemplul 1: Toate fișierele unui proiect

package main

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

func main() {
    os.MkdirAll("proiect/cmd/server", 0o755)
    os.MkdirAll("proiect/intern/baza", 0o755)
    os.WriteFile("proiect/go.mod", nil, 0o644)
    os.WriteFile("proiect/cmd/server/main.go", nil, 0o644)
    os.WriteFile("proiect/intern/baza/baza.go", nil, 0o644)

    err := filepath.WalkDir("proiect", func(cale string, d fs.DirEntry, err error) error {
        if err != nil {
            return err
        }
        if d.IsDir() {
            fmt.Println("[dir] ", cale)
        } else {
            fmt.Println("      ", cale)
        }
        return nil
    })
    fmt.Println("eroare:", err)
}

Rezultat

[dir]  proiect
[dir]  proiect/cmd
[dir]  proiect/cmd/server
       proiect/cmd/server/main.go
       proiect/go.mod
[dir]  proiect/intern
[dir]  proiect/intern/baza
       proiect/intern/baza/baza.go
eroare: <nil>

Exemplul 2: Sărirea directoarelor și mărimea totală

Sărim peste .git și node_modules cu SkipDir și adunăm mărimea fișierelor .js.

package main

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

func main() {
    os.MkdirAll("site/.git/objects", 0o755)
    os.MkdirAll("site/node_modules/react", 0o755)
    os.MkdirAll("site/js", 0o755)
    os.WriteFile("site/.git/objects/a1.js", make([]byte, 9000), 0o644)
    os.WriteFile("site/node_modules/react/index.js", make([]byte, 50000), 0o644)
    os.WriteFile("site/js/app.js", make([]byte, 1200), 0o644)
    os.WriteFile("site/js/meniu.js", make([]byte, 300), 0o644)

    var total int64
    filepath.WalkDir("site", func(cale string, d fs.DirEntry, err error) error {
        if err != nil {
            return err
        }
        if d.IsDir() && (d.Name() == ".git" || d.Name() == "node_modules") {
            fmt.Println("sar peste", cale)
            return filepath.SkipDir
        }
        if filepath.Ext(cale) == ".js" {
            info, _ := d.Info()
            total += info.Size()
            fmt.Println("număr", cale)
        }
        return nil
    })
    fmt.Println("total JS:", total, "octeți")
}

Rezultat

sar peste site/.git
număr site/js/app.js
număr site/js/meniu.js
sar peste site/node_modules
total JS: 1500 octeți

Exemplul 3: Oprire la primul rezultat

package main

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

func main() {
    os.MkdirAll("documente/2024", 0o755)
    os.MkdirAll("documente/2025", 0o755)
    os.WriteFile("documente/2024/contract.pdf", nil, 0o644)
    os.WriteFile("documente/2025/contract.pdf", nil, 0o644)

    var gasit string
    filepath.WalkDir("documente", func(cale string, d fs.DirEntry, err error) error {
        if d.Name() == "contract.pdf" {
            gasit = cale
            return filepath.SkipAll
        }
        return nil
    })
    fmt.Println("primul contract:", gasit)
}

Rezultat

primul contract: documente/2024/contract.pdf

Greșeli frecvente

  • Ignori parametrul err și folosești d. Când err != nil, d poate fi nil și programul se oprește cu panică.
  • Returnezi SkipDir pentru un fișier și te miri că restul directorului dispare. Pentru fișiere, SkipDir sare peste toate fișierele rămase din același director.
  • Folosești filepath.Walk în cod nou. WalkDir este mai rapid.

Comenzi înrudite

  • filepath.Walk: Parcurge recursiv un director și apelează o funcție cu informațiile complete (FileInfo) ale fiecărei intrări.
  • filepath.Glob: Returnează toate fișierele de pe disc ale căror nume se potrivesc unui model cu *, ? și [ ].
  • os.File.ReadDir: Citește intrările (fișiere și subdirectoare) dintr-un director deschis.
  • filepath.Rel: Calculează calea relativă care duce de la directorul basepath la targpath.