Skip to content

Commit

Permalink
Merge branch 'noninteractive'
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Milde committed Jan 12, 2021
2 parents eb2c823 + 15c29bf commit 595a721
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 10 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ test:
coverage:
go test -v -race -coverprofile=coverage.txt -covermode=atomic $(PACKAGES)

coverage-html: coverage
go tool cover -html=coverage.txt

benchnmark:
go test -bench=. $(PACKAGES)

Expand All @@ -34,4 +37,4 @@ clean:
-rm -r build
-sudo rm -r obj-x86_64-linux-gnu on *.deb

.PHONY: run build test coverage clean
.PHONY: run build test coverage coverage-html clean
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,19 @@ Debian:

## Usage

gdu # show all mounted disks
gdu some_dir_to_analyze # analyze given dir
gdu -log-file=./gdu.log some_dir # write errors to log file
gdu -ignore-dir=/sys,/proc / # ignore some paths
gdu -no-color / # use only white/gray/black colors
gdu # show all mounted disks
gdu some_dir_to_analyze # analyze given dir
gdu -log-file=./gdu.log some_dir # write errors to log file
gdu -ignore-dir=/sys,/proc / # ignore some paths
gdu -no-color / # use only white/gray/black colors

gdu -non-interactive / # only print stats, do not start interactive mode
gdu -non-interactive -no-progress / # do not show progress either
gdu / > file # write stats to file, do not start interactive mode

Gdu has two modes: interactive (default) and non-interactive.

Non-interactive mode is started automtically when TTY is not detected (using [go-isatty](https://github.com/mattn/go-isatty)), for example if the output is being piped to a file, or it can be started explicitly by using a flag.

## Running tests

Expand All @@ -76,4 +83,4 @@ duc index / | 34 | 4.5 | 11.3 (2.5 + 8.8)
baobab / | 38 | 12 | 25 (16 + 9)
ncdu / | 43 | 13 | 18.5 (1.5 + 17)

Gdu is inspired by [ncdu](https://dev.yorhel.nl/ncdu), [godu](https://github.com/viktomas/godu), [dua](https://github.com/Byron/dua-cli) and [df](https://www.gnu.org/software/coreutils/manual/html_node/df-invocation.html).
Gdu is inspired by [ncdu](https://dev.yorhel.nl/ncdu), [godu](https://github.com/viktomas/godu), [dua](https://github.com/Byron/dua-cli) and [df](https://www.gnu.org/software/coreutils/manual/html_node/df-invocation.html).
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ go 1.15

require (
github.com/gdamore/tcell/v2 v2.1.0
github.com/gookit/color v1.3.6
github.com/mattn/go-isatty v0.0.12
github.com/rivo/tview v0.0.0-20201204190810-5406288b8e4e
github.com/stretchr/testify v1.6.1
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gdamore/tcell/v2 v2.1.0 h1:UnSmozHgBkQi2PGsFr+rpdXuAPRRucMegpQp3Z3kDro=
github.com/gdamore/tcell/v2 v2.1.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gookit/color v1.3.6 h1:Rgbazd4JO5AgSTVGS3o0nvaSdwdrS8bzvIXwtK6OiMk=
github.com/gookit/color v1.3.6/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
Expand All @@ -17,9 +21,11 @@ github.com/rivo/tview v0.0.0-20201204190810-5406288b8e4e/go.mod h1:0ha5CGekam8ZV
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
21 changes: 18 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import (

"github.com/dundee/gdu/analyze"
"github.com/dundee/gdu/cli"
"github.com/dundee/gdu/stdout"
"github.com/gdamore/tcell/v2"
"github.com/mattn/go-isatty"
"github.com/rivo/tview"
)

Expand All @@ -22,13 +24,18 @@ func main() {
ignoreDirPaths := flag.String("ignore-dir", "/proc,/dev,/sys,/run", "Absolute paths to ignore (separated by comma)")
showVersion := flag.Bool("v", false, "Prints version")
noColor := flag.Bool("no-color", false, "Do not use colorized output")
nonInteractive := flag.Bool("non-interactive", false, "Do not run in interactive mode")
noProgress := flag.Bool("no-progress", false, "Do not show progress")

flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of gdu: [flags] [directory to scan]\n")
flag.PrintDefaults()
}

flag.Parse()
args := flag.Args()

istty := isatty.IsTerminal(os.Stdout.Fd())

if *showVersion {
fmt.Println("Version:\t", AppVersion)
Expand All @@ -40,9 +47,19 @@ func main() {
log.Fatalf("error opening file: %v", err)
}
defer f.Close()

log.SetOutput(f)

if *nonInteractive || !istty {
if len(args) == 1 {
ui := stdout.CreateStdoutUI(os.Stdout, !*noColor && istty, !*noProgress && istty)
ui.SetIgnoreDirPaths(strings.Split(*ignoreDirPaths, ","))
ui.AnalyzePath(args[0], analyze.ProcessDir)
} else {
flag.Usage()
}
return
}

screen, err := tcell.NewScreen()
if err != nil {
panic(err)
Expand All @@ -56,8 +73,6 @@ func main() {
ui := cli.CreateUI(screen, !*noColor)
ui.SetIgnoreDirPaths(strings.Split(*ignoreDirPaths, ","))

args := flag.Args()

if len(args) == 1 {
ui.AnalyzePath(args[0], analyze.ProcessDir, nil)
} else {
Expand Down
167 changes: 167 additions & 0 deletions stdout/stdout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package stdout

import (
"fmt"
"io"
"math"
"path/filepath"
"sort"
"sync"
"time"

"github.com/dundee/gdu/analyze"
"github.com/gookit/color"
)

// UI struct
type UI struct {
output io.Writer
ignoreDirPaths map[string]bool
useColors bool
showProgress bool
red color.Style
orange color.Style
blue color.Style
}

// CreateStdoutUI creates UI for stdout
func CreateStdoutUI(output io.Writer, useColors bool, showProgress bool) *UI {
ui := &UI{
output: output,
useColors: useColors,
showProgress: showProgress,
}

ui.red = color.Style{color.FgRed, color.OpBold}
ui.orange = color.Style{color.FgYellow, color.OpBold}
ui.blue = color.Style{color.FgBlue, color.OpBold}

if !useColors {
color.Disable()
}

return ui
}

// AnalyzePath analyzes recursively disk usage in given path
func (ui *UI) AnalyzePath(path string, analyzer analyze.Analyzer) {
abspath, _ := filepath.Abs(path)
var dir *analyze.File

progress := &analyze.CurrentProgress{
Mutex: &sync.Mutex{},
Done: false,
ItemCount: 0,
TotalSize: int64(0),
}
var wait sync.WaitGroup

if ui.showProgress {
wait.Add(1)
go func() {
ui.updateProgress(progress)
wait.Done()
}()
}

wait.Add(1)
go func() {
dir = analyzer(abspath, progress, ui.ShouldDirBeIgnored)
wait.Done()
}()

wait.Wait()

sort.Sort(dir.Files)

var lineFormat string
if ui.useColors {
lineFormat = "%20s %s\n"
} else {
lineFormat = "%9s %s\n"
}

for _, file := range dir.Files {
if file.IsDir {
fmt.Fprintf(ui.output,
lineFormat,
ui.formatSize(file.Size),
ui.blue.Sprintf("/"+file.Name))
} else {
fmt.Fprintf(ui.output,
lineFormat,
ui.formatSize(file.Size),
file.Name)
}
}
}

// SetIgnoreDirPaths sets paths to ignore
func (ui *UI) SetIgnoreDirPaths(paths []string) {
ui.ignoreDirPaths = make(map[string]bool, len(paths))
for _, path := range paths {
ui.ignoreDirPaths[path] = true
}
}

// ShouldDirBeIgnored returns true if given path should be ignored
func (ui *UI) ShouldDirBeIgnored(path string) bool {
return ui.ignoreDirPaths[path]
}

func (ui *UI) updateProgress(progress *analyze.CurrentProgress) {
emptyRow := "\r"
for j := 0; j < 100; j++ {
emptyRow += " "
}

i := 0
for {
progress.Mutex.Lock()

fmt.Fprint(ui.output, emptyRow)

if progress.Done {
fmt.Fprint(ui.output, "\r")
return
}

switch i {
case 0:
fmt.Fprint(ui.output, "\r | ")
break
case 1:
fmt.Fprint(ui.output, "\r / ")
break
case 2:
fmt.Fprint(ui.output, "\r - ")
break
case 3:
fmt.Fprint(ui.output, "\r \\ ")
break
}

fmt.Fprint(ui.output, "Scanning... Total items: "+
ui.red.Sprint(progress.ItemCount)+
" size: "+
ui.formatSize(progress.TotalSize))
progress.Mutex.Unlock()

time.Sleep(100 * time.Millisecond)
i++
i %= 4
}
}

func (ui *UI) formatSize(size int64) string {
if size > 1e12 {
return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 40)) + " TiB"
} else if size > 1e9 {
return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 30)) + " GiB"
} else if size > 1e6 {
return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 20)) + " MiB"
} else if size > 1e3 {
return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 10)) + " KiB"
}
return ui.orange.Sprintf("%d", size) + " B"
}
56 changes: 56 additions & 0 deletions stdout/stdout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package stdout

import (
"bytes"
"testing"

"github.com/dundee/gdu/analyze"
"github.com/stretchr/testify/assert"
)

func TestAnalyzePath(t *testing.T) {
fin := analyze.CreateTestDir()
defer fin()

buff := make([]byte, 10)
output := bytes.NewBuffer(buff)

ui := CreateStdoutUI(output, false, false)
ui.SetIgnoreDirPaths([]string{"/xxx"})
ui.AnalyzePath("test_dir", analyze.ProcessDir)

assert.Contains(t, output.String(), "nested")
}

func TestAnalyzePathWithColors(t *testing.T) {
fin := analyze.CreateTestDir()
defer fin()

buff := make([]byte, 10)
output := bytes.NewBuffer(buff)

ui := CreateStdoutUI(output, true, false)
ui.SetIgnoreDirPaths([]string{"/xxx"})
ui.AnalyzePath("test_dir/nested", analyze.ProcessDir)

assert.Contains(t, output.String(), "subnested")
}

func TestAnalyzePathWithProgress(t *testing.T) {
fin := analyze.CreateTestDir()
defer fin()

output := bytes.NewBuffer(make([]byte, 10))

ui := CreateStdoutUI(output, false, true)
ui.SetIgnoreDirPaths([]string{"/xxx"})
ui.AnalyzePath("test_dir", analyze.ProcessDir)

assert.Contains(t, output.String(), "nested")
}

func printBuffer(buff *bytes.Buffer) {
for i, x := range buff.String() {
println(i, string(x))
}
}

0 comments on commit 595a721

Please sign in to comment.