Add sfdupes implementation: scan, report, and trees subcommands
This commit is contained in:
81
main.go
Normal file
81
main.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user