diff --git a/README.md b/README.md index a2d59bc..58b7dcb 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ go install github.com/datumbrain/dedup@latest # Scan current directory dedup +# Recursive scan (includes subdirectories) +dedup -r . +dedup -r /path/to/folder +dedup --recursive "C:\Users\Username\Downloads" + # Scan specific directory dedup /path/to/folder dedup "C:\Users\Username\Downloads" @@ -25,7 +30,7 @@ dedup "C:\Users\Username\Downloads" - **Intelligent Recommendations**: Automatically identifies which files to keep vs delete - **Platform-Specific Commands**: Generates deletion commands for your OS (Windows/macOS/Linux) - **Safe by Default**: Only scans files, never deletes anything automatically -- **Non-Recursive**: Only scans the specified folder (doesn't go into subdirectories) +- **Recursive**: Can scan subdirectories if the -r / --recursive flag is used ## How It Works diff --git a/main.go b/main.go index b9c0197..e2dca2f 100644 --- a/main.go +++ b/main.go @@ -232,37 +232,42 @@ func generateDeletionCommands(filesToDelete []*FileInfo) { } // findDuplicates finds all duplicate files in the specified folder -func findDuplicates(folderPath string) error { +func findDuplicates(folderPath string, recursive bool) error { // Map to store checksum -> list of files with that checksum checksumMap := make(map[string][]*FileInfo) - // Read directory contents - entries, err := os.ReadDir(folderPath) - if err != nil { - return fmt.Errorf("error reading directory: %v", err) + if recursive { + fmt.Printf("Scanning files recursively in: %s\n", folderPath) + } else { + fmt.Printf("Scanning files in: %s\n", folderPath) } - - fmt.Printf("Scanning files in: %s\n", folderPath) fmt.Println("Calculating checksums...") - // Process each file (skip directories) - for _, entry := range entries { - if entry.IsDir() { - continue // Skip directories + err := filepath.Walk(folderPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + fmt.Printf("Warning: Could not access %s: %v\n", path, err) + return nil + } + if info.IsDir() { + if !recursive && path != folderPath { + return filepath.SkipDir + } + return nil } - filePath := filepath.Join(folderPath, entry.Name()) - - fmt.Printf("Processing: %s\n", entry.Name()) + fmt.Printf("Processing: %s\n", path) - fileInfo, err := getFileInfo(filePath) + fileInfo, err := getFileInfo(path) if err != nil { - fmt.Printf("Warning: Could not process %s: %v\n", filePath, err) - continue + fmt.Printf("Warning: Could not process %s: %v\n", path, err) + return nil } - // Add to checksum map checksumMap[fileInfo.Checksum] = append(checksumMap[fileInfo.Checksum], fileInfo) + return nil + }) + if err != nil { + return fmt.Errorf("error scanning directory: %v", err) } // Find and display duplicates @@ -335,10 +340,15 @@ func findDuplicates(folderPath string) error { } func main() { - // Get folder path from command line argument or use current directory folderPath := "." - if len(os.Args) > 1 { - folderPath = os.Args[1] + recursive := false + + for _, arg := range os.Args[1:] { + if arg == "--recursive" || arg == "-r" { + recursive = true + } else { + folderPath = arg + } } // Verify the path exists and is a directory @@ -354,7 +364,7 @@ func main() { } // Find duplicates - if err := findDuplicates(folderPath); err != nil { + if err := findDuplicates(folderPath, recursive); err != nil { fmt.Printf("Error: %v\n", err) os.Exit(1) }