82 lines
2.3 KiB
Go
82 lines
2.3 KiB
Go
// 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 [-root /srv] [-workers N] > 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"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func main() {
|
|
root := &cobra.Command{
|
|
Use: "sfdupes",
|
|
Short: "Find candidate duplicate files by size and head/tail SHA-256",
|
|
Args: cobra.NoArgs,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
// A missing subcommand prints usage and exits 2.
|
|
_ = cmd.Usage()
|
|
os.Exit(2)
|
|
},
|
|
}
|
|
// 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
|
|
|
|
scanCmd := &cobra.Command{
|
|
Use: "scan [-root /srv] [-workers N]",
|
|
Short: "Walk a tree and emit one record per regular file on stdout",
|
|
// The README specifies single-dash flags (-root, -workers);
|
|
// parse them with the stdlib flag package inside runScan.
|
|
DisableFlagParsing: true,
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
runScan(args)
|
|
},
|
|
}
|
|
|
|
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(cmd *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(cmd *cobra.Command, args []string) {
|
|
runTrees(args)
|
|
},
|
|
}
|
|
|
|
root.AddCommand(scanCmd, reportCmd, treesCmd)
|
|
if err := root.Execute(); err != nil {
|
|
// Cobra has already printed the error and usage to stderr;
|
|
// an invalid subcommand or bad arguments is a usage error.
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
|
|
// fatalf reports a fatal error and exits 1.
|
|
func fatalf(format string, args ...any) {
|
|
fmt.Fprintf(os.Stderr, "sfdupes: "+format+"\n", args...)
|
|
os.Exit(1)
|
|
}
|