Help us shape the future of Go! Take the 2026 Go Developer Survey

files

package
v0.25.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 4, 2025 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var FilesCmd = &cobra.Command{
	Use:   "files",
	Short: "A command for managing files",
	Long:  `This command will help manage various aspects of file operations on MacOS and Linux systems.`,
	Run: func(cmd *cobra.Command, args []string) {
		err := cmd.Help()
		cobra.CheckErr(err)
	},
}
View Source
var FindAndDeleteCmd = &cobra.Command{
	Use:   "findAndDelete [directory]",
	Short: "Find and delete files of selected types, or list extensions",
	Long: `Recursively scan the provided directory for files of types selected by the user
and delete them after an interactive confirmation. Use --list-extensions to list
all file extensions in the directory instead.
`,
	Args: cobra.ExactArgs(1),
	Run: func(cmd *cobra.Command, args []string) {
		dir := args[0]
		isVerbose := utils.IsVerbose(cmd)

		if _, err := os.Stat(dir); os.IsNotExist(err) {
			log.Error("Provided directory does not exist: %s", dir)
			return
		}

		if listExtensions {
			extensions, err := ListExtensions(dir)
			if err != nil {
				log.Error("Error listing extensions: %v", err)
				return
			}
			log.Message("File extensions found in %s:", dir)
			for _, ext := range extensions {
				log.Message("  - %s", ext)
			}
			return
		}

		matchFn, err := buildMatchFunction(globPattern, extension)
		if err != nil {
			log.Error("Error building match function: %v", err)
			return
		}
		if matchFn == nil {

			options := []string{
				"JSON files (.json)",
				"Video files (.mp4, .mov, .avi, .mkv)",
				"Image files (.jpg, .jpeg, .png, .gif)",
				"System files (.DS_Store)",
			}
			var selected []string
			prompt := &survey.MultiSelect{
				Message: "Select file types to find and delete:",
				Options: options,
			}
			if err := survey.AskOne(prompt, &selected); err != nil {
				log.Error("Error collecting selection: %v", err)
				return
			}

			if len(selected) == 0 {
				log.Info("No file types selected. Nothing to do.")
				return
			}

			extLookup := map[string]bool{}
			for _, s := range selected {
				s = strings.ToLower(s)
				if strings.Contains(s, "json") {
					extLookup[".json"] = true
				}
				if strings.Contains(s, "video") {
					extLookup[".mp4"] = true
					extLookup[".mov"] = true
					extLookup[".avi"] = true
					extLookup[".mkv"] = true
				}
				if strings.Contains(s, "image") {
					extLookup[".jpg"] = true
					extLookup[".jpeg"] = true
					extLookup[".png"] = true
					extLookup[".gif"] = true
				}
				if strings.Contains(s, "system") {
					extLookup[".ds_store"] = true
				}
			}
			matchFn = func(name string) bool {
				return extLookup[strings.ToLower(filepath.Ext(name))]
			}
		}

		log.Start("Scanning for files...")
		spinner := utils.NewProgressSpinner("Scanning directories...")
		matches, totalSize, walkErr := ScanFiles(dir, matchFn, spinner)

		if walkErr != nil {
			spinner.Stop()
			log.Error("Error scanning directory: %v", walkErr)
			return
		}

		spinner.UpdateMessage("Scan complete.")
		spinner.SetProgressBar(1.0)
		spinner.Stop()

		if walkErr != nil {
			log.Error("Error scanning directory: %v", walkErr)
			return
		}

		if len(matches) == 0 {
			log.Success("No matching files found in %s.", dir)
			return
		}

		log.Message("\nFound %d file(s) (%.2f MB total):", len(matches), float64(totalSize)/(1024*1024))
		for _, m := range matches {
			if isVerbose {
				log.Message("  - %s", m)
			} else {
				log.Message("  - %s", filepath.Base(m))
			}
		}

		confirm := false
		promptConfirm := &survey.Confirm{
			Message: fmt.Sprintf("Delete %d file(s) (%.2f MB)?", len(matches), float64(totalSize)/(1024*1024)),
			Default: false,
		}
		if err := survey.AskOne(promptConfirm, &confirm); err != nil {
			log.Error("Error during confirmation prompt: %v", err)
			return
		}
		if !confirm {
			log.Info("Deletion cancelled by user.")
			return
		}

		// Attempt parallel deletion
		var deleted, errors atomic.Int64
		var wg sync.WaitGroup
		workerCount := 4
		fileChan := make(chan string, len(matches))

		for i := 0; i < workerCount; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for f := range fileChan {
					if f == "" {
						continue
					}
					if err := os.Remove(f); err != nil {
						if !os.IsNotExist(err) {
							log.Error("Failed to delete %s: %v", f, err)
							errors.Add(1)
						}
					} else {
						if isVerbose {
							log.Success("Deleted %s", f)
						}
						deleted.Add(1)
					}
				}
			}()
		}

		for _, f := range matches {
			fileChan <- f
		}
		close(fileChan)

		wg.Wait()

		log.Success("Done. Deleted %d file(s), %d errors.", deleted.Load(), errors.Load())
	},
}

Functions

func ListExtensions added in v0.25.16

func ListExtensions(dir string) ([]string, error)

func ScanFiles

func ScanFiles(dir string, matchFn func(name string) bool, spinner *utils.Spinner) ([]string, int64, error)

Types

type FileTypeCategory

type FileTypeCategory struct {
	Name       string
	Extensions map[string]bool
	Options    []string // For display in the survey
}

FileTypeCategory represents a category of file types with their extensions

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL