// Command sfdupes quickly identifies candidate duplicate files across // very large filesystems without reading full file contents. Files are // considered duplicates when they have identical size, identical SHA-256 // of their first 1024 bytes, and identical SHA-256 of their last 1024 // bytes. // // Usage: // // sfdupes scan [--workers N] [-x] PATH... > files.dat // sfdupes report [files.dat|-] > dupes.tsv // sfdupes trees [files.dat|-] > dupetrees.tsv // // See README.md for the complete specification. package main import ( "fmt" "os" "runtime" "github.com/spf13/cobra" ) // Exit codes: 0 is success (even with per-file warnings), exitFatal is // a fatal error, exitUsage is a usage error. const ( exitFatal = 1 exitUsage = 2 ) // Version is the build version, injected at link time via -ldflags // (see the Makefile); "dev" for a plain go build. // //nolint:gochecknoglobals // written only by the linker var Version = "dev" func main() { root := &cobra.Command{ Use: "sfdupes", Short: "Find candidate duplicate files by size and head/tail SHA-256", Version: Version, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, _ []string) { // A missing subcommand prints usage and exits 2. _ = cmd.Usage() os.Exit(exitUsage) }, } // Everything on stdout is machine-readable data; all human-facing // output (help, usage, errors) goes to stderr. root.SetOut(os.Stderr) root.SetErr(os.Stderr) root.CompletionOptions.DisableDefaultCmd = true var ( scanWorkers int scanOneFS bool ) scanCmd := &cobra.Command{ Use: "scan [--workers N] [-x] PATH...", Short: "Walk trees and emit one record per regular file on stdout", Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { runScan(args, scanWorkers, scanOneFS) }, } scanCmd.Flags().IntVar(&scanWorkers, "workers", runtime.NumCPU(), "concurrent workers for the stat and hash passes") scanCmd.Flags().BoolVarP(&scanOneFS, "one-file-system", "x", false, "do not cross filesystem boundaries") reportCmd := &cobra.Command{ Use: "report [files.dat|-]", Short: "Read a scan stream and print the file-level duplicates report", Args: cobra.MaximumNArgs(1), Run: func(_ *cobra.Command, args []string) { runReport(args) }, } treesCmd := &cobra.Command{ Use: "trees [files.dat|-]", Short: "Read a scan stream and print the duplicate-tree report", Args: cobra.MaximumNArgs(1), Run: func(_ *cobra.Command, args []string) { runTrees(args) }, } root.AddCommand(scanCmd, reportCmd, treesCmd) err := root.Execute() if err != nil { // Cobra has already printed the error and usage to stderr; // an invalid subcommand or bad arguments is a usage error. os.Exit(exitUsage) } } // fatalf reports a fatal error and exits 1. func fatalf(format string, args ...any) { fmt.Fprintf(os.Stderr, "sfdupes: "+format+"\n", args...) os.Exit(exitFatal) }