Skip to content

Commit

Permalink
Merge pull request #391 from dundee/dundee/feat/top-files
Browse files Browse the repository at this point in the history
feat: show top largest files
  • Loading branch information
dundee authored Nov 30, 2024
2 parents 8c2680b + f69bda9 commit 3f37bc1
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 22 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Flags:
--si Show sizes with decimal SI prefixes (kB, MB, GB) instead of binary prefixes (KiB, MiB, GiB)
--storage-path string Path to persistent key-value storage directory (default "/tmp/badger")
-s, --summarize Show only a total in non-interactive mode
-t, --top int Show only top X largest files in non-interactive mode
--use-storage Use persistent key-value storage for analysis data (experimental)
-v, --version Print version
--write-config Write current configuration to file (default is $HOME/.gdu.yaml)
Expand Down Expand Up @@ -104,6 +105,7 @@ Basic list of actions in interactive mode (show help modal for more):
gdu -n / # only print stats, do not start interactive mode
gdu -np / # do not show progress, useful when using its output in a script
gdu -nps /some/dir # show only total usage for given dir
gdu -nt 10 / # show top 10 largest files
gdu / > file # write stats to file, do not start interactive mode

gdu -o- / | gzip -c >report.json.gz # write all info to JSON file for later analysis
Expand Down
2 changes: 2 additions & 0 deletions cmd/gdu/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type Flags struct {
StoragePath string `yaml:"storage-path"`
ReadFromStorage bool `yaml:"read-from-storage"`
Summarize bool `yaml:"summarize"`
Top int `yaml:"top"`
UseSIPrefix bool `yaml:"use-si-prefix"`
NoPrefix bool `yaml:"no-prefix"`
WriteConfig bool `yaml:"-"`
Expand Down Expand Up @@ -242,6 +243,7 @@ func (a *App) createUI() (UI, error) {
a.Flags.ConstGC,
a.Flags.UseSIPrefix,
a.Flags.NoPrefix,
a.Flags.Top,
)
if a.Flags.NoUnicode {
stdoutUI.UseOldProgressRunes()
Expand Down
1 change: 1 addition & 0 deletions cmd/gdu/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func init() {
flags.BoolVarP(&af.NoProgress, "no-progress", "p", false, "Do not show progress in non-interactive mode")
flags.BoolVarP(&af.NoUnicode, "no-unicode", "u", false, "Do not use Unicode symbols (for size bar)")
flags.BoolVarP(&af.Summarize, "summarize", "s", false, "Show only a total in non-interactive mode")
flags.IntVarP(&af.Top, "top", "t", 0, "Show only top X largest files in non-interactive mode")
flags.BoolVar(&af.UseSIPrefix, "si", false, "Show sizes with decimal SI prefixes (kB, MB, GB) instead of binary prefixes (KiB, MiB, GiB)")
flags.BoolVar(&af.NoPrefix, "no-prefix", false, "Show sizes as raw numbers without any prefixes (SI or binary) in non-interactive mode")
flags.BoolVar(&af.NoMouse, "no-mouse", false, "Do not use mouse")
Expand Down
2 changes: 2 additions & 0 deletions gdu.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ non-interactive mode

**-s**, **\--summarize**\[=false\] Show only a total in non-interactive mode

**-t**, **\--top**\[=0\] Show only top X largest files in non-interactive mode

**-d**, **\--show-disks**\[=false\] Show all mounted disks

**-a**, **\--show-apparent-size**\[=false\] Show apparent size
Expand Down
53 changes: 53 additions & 0 deletions pkg/analyze/top.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package analyze

import (
"sort"

"github.com/dundee/gdu/v5/pkg/fs"
)

// TopList is a list of top largest files
type TopList struct {
Count int
Items fs.Files
MinSize int64
}

// NewTopList creates new TopList
func NewTopList(count int) *TopList {
return &TopList{Count: count}
}

// Add adds file to the list
func (tl *TopList) Add(file fs.Item) {
if len(tl.Items) < tl.Count {
tl.Items = append(tl.Items, file)
if file.GetSize() > tl.MinSize {
tl.MinSize = file.GetSize()
}
} else if file.GetSize() > tl.MinSize {
tl.Items = append(tl.Items, file)
tl.MinSize = file.GetSize()
if len(tl.Items) > tl.Count {
sort.Sort(fs.ByApparentSize(tl.Items))
tl.Items = tl.Items[1:]
}
}
}

func CollectTopFiles(dir fs.Item, count int) fs.Files {
topList := NewTopList(count)
walkDir(dir, topList)
sort.Sort(sort.Reverse(fs.ByApparentSize(topList.Items)))
return topList.Items
}

func walkDir(dir fs.Item, topList *TopList) {
for _, item := range dir.GetFiles() {
if item.IsDir() {
walkDir(item, topList)
} else {
topList.Add(item)
}
}
}
38 changes: 38 additions & 0 deletions pkg/analyze/top_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package analyze

import (
"testing"

"github.com/dundee/gdu/v5/internal/testdir"
"github.com/stretchr/testify/assert"
)

func TestCollectTopFiles2(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()

dir := CreateAnalyzer().AnalyzeDir(
"test_dir", func(_, _ string) bool { return false }, false,
)

topFiles := CollectTopFiles(dir, 2)
assert.Equal(t, 2, len(topFiles))
assert.Equal(t, "file", topFiles[0].GetName())
assert.Equal(t, int64(5), topFiles[0].GetSize())
assert.Equal(t, "file2", topFiles[1].GetName())
assert.Equal(t, int64(2), topFiles[1].GetSize())
}

func TestCollectTopFiles1(t *testing.T) {
fin := testdir.CreateTestDir()
defer fin()

dir := CreateAnalyzer().AnalyzeDir(
"test_dir", func(_, _ string) bool { return false }, false,
)

topFiles := CollectTopFiles(dir, 1)
assert.Equal(t, 1, len(topFiles))
assert.Equal(t, "file", topFiles[0].GetName())
assert.Equal(t, int64(5), topFiles[0].GetSize())
}
45 changes: 41 additions & 4 deletions stdout/stdout.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type UI struct {
blue *color.Color
summarize bool
noPrefix bool
top int
}

var (
Expand All @@ -45,6 +46,7 @@ func CreateStdoutUI(
constGC bool,
useSIPrefix bool,
noPrefix bool,
top int,
) *UI {
ui := &UI{
UI: &common.UI{
Expand All @@ -59,6 +61,7 @@ func CreateStdoutUI(
output: output,
summarize: summarize,
noPrefix: noPrefix,
top: top,
}

ui.red = color.New(color.FgRed).Add(color.Bold)
Expand Down Expand Up @@ -167,9 +170,12 @@ func (ui *UI) AnalyzePath(path string, _ fs.Item) error {

wait.Wait()

if ui.summarize {
switch {
case ui.top > 0:
ui.printTopFiles(dir)
case ui.summarize:
ui.printTotalItem(dir)
} else {
default:
ui.showDir(dir)
}

Expand All @@ -187,9 +193,12 @@ func (ui *UI) ReadFromStorage(storagePath, path string) error {
return err
}

if ui.summarize {
switch {
case ui.top > 0:
ui.printTopFiles(dir)
case ui.summarize:
ui.printTotalItem(dir)
} else {
default:
ui.showDir(dir)
}
return nil
Expand All @@ -203,6 +212,13 @@ func (ui *UI) showDir(dir fs.Item) {
}
}

func (ui *UI) printTopFiles(file fs.Item) {
collected := analyze.CollectTopFiles(file, ui.top)
for _, file := range collected {
ui.printItemPath(file)
}
}

func (ui *UI) printTotalItem(file fs.Item) {
var lineFormat string
if ui.UseColors {
Expand Down Expand Up @@ -256,6 +272,27 @@ func (ui *UI) printItem(file fs.Item) {
}
}

func (ui *UI) printItemPath(file fs.Item) {
var lineFormat string
if ui.UseColors {
lineFormat = "%20s %s\n"
} else {
lineFormat = "%9s %s\n"
}

var size int64
if ui.ShowApparentSize {
size = file.GetSize()
} else {
size = file.GetUsage()
}

fmt.Fprintf(ui.output,
lineFormat,
ui.formatSize(size),
file.GetPath())
}

// ReadAnalysis reads analysis report from JSON file
func (ui *UI) ReadAnalysis(input io.Reader) error {
var (
Expand Down
2 changes: 1 addition & 1 deletion stdout/stdout_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestShowDevicesWithErr(t *testing.T) {
output := bytes.NewBuffer(make([]byte, 10))

getter := device.LinuxDevicesInfoGetter{MountsPath: "/xyzxyz"}
ui := CreateStdoutUI(output, false, true, false, false, false, false, false, false)
ui := CreateStdoutUI(output, false, true, false, false, false, false, false, false, 0)
err := ui.ListDevices(getter)

assert.Contains(t, err.Error(), "no such file")
Expand Down
Loading

0 comments on commit 3f37bc1

Please sign in to comment.