Skip to content

Commit 32d380b

Browse files
committed
added posibility to ignore hidden dirs and ignore dirs by regular pattern
closes #50
1 parent e51bbfd commit 32d380b

File tree

21 files changed

+449
-178
lines changed

21 files changed

+449
-178
lines changed

README.md

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,19 @@ Using curl:
7373
gdu [flags] [directory_to_scan]
7474
7575
Flags:
76-
-h, --help help for gdu
77-
-i, --ignore-dirs strings Absolute paths to ignore (separated by comma) (default [/proc,/dev,/sys,/run])
78-
-l, --log-file string Path to a logfile (default "/dev/null")
79-
-m, --max-cores int Set max cores that GDU will use. 8 cores available (default 8)
80-
-c, --no-color Do not use colorized output
81-
-x, --no-cross Do not cross filesystem boundaries
82-
-p, --no-progress Do not show progress in non-interactive mode
83-
-n, --non-interactive Do not run in interactive mode
84-
-a, --show-apparent-size Show apparent size
85-
-d, --show-disks Show all mounted disks
86-
-v, --version Print version
76+
-h, --help help for gdu
77+
-i, --ignore-dirs strings Absolute paths to ignore (separated by comma) (default [/proc,/dev,/sys,/run])
78+
-I, --ignore-dirs-pattern strings Absolute path patterns to ignore (separated by comma)
79+
-l, --log-file string Path to a logfile (default "/dev/null")
80+
-m, --max-cores int Set max cores that GDU will use. 8 cores available (default 8)
81+
-c, --no-color Do not use colorized output
82+
-x, --no-cross Do not cross filesystem boundaries
83+
-H, --no-hidden Ignore hidden directories (beggining with dot)
84+
-p, --no-progress Do not show progress in non-interactive mode
85+
-n, --non-interactive Do not run in interactive mode
86+
-a, --show-apparent-size Show apparent size
87+
-d, --show-disks Show all mounted disks
88+
-v, --version Print version
8789
```
8890

8991
## Examples
@@ -94,6 +96,7 @@ Flags:
9496
gdu -d # show all mounted disks
9597
gdu -l ./gdu.log <some_dir> # write errors to log file
9698
gdu -i /sys,/proc / # ignore some paths
99+
gdu -I '.*[abc]+' # ignore paths by regular pattern
97100
gdu -c / # use only white/gray/black colors
98101

99102
gdu -n / # only print stats, do not start interactive mode

analyze/dir.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type CurrentProgress struct {
1717
var concurrencyLimit chan struct{} = make(chan struct{}, 3*runtime.GOMAXPROCS(0))
1818

1919
// ShouldDirBeIgnored whether path should be ignored
20-
type ShouldDirBeIgnored func(path string) bool
20+
type ShouldDirBeIgnored func(name, path string) bool
2121

2222
// Analyzer is type for dir analyzing function
2323
type Analyzer interface {
@@ -114,9 +114,10 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir {
114114
}
115115

116116
for _, f := range files {
117-
entryPath := filepath.Join(path, f.Name())
117+
name := f.Name()
118+
entryPath := filepath.Join(path, name)
118119
if f.IsDir() {
119-
if a.ignoreDir(entryPath) {
120+
if a.ignoreDir(name, entryPath) {
120121
continue
121122
}
122123
dirCount++
@@ -136,7 +137,7 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir {
136137
continue
137138
}
138139
file = &File{
139-
Name: f.Name(),
140+
Name: name,
140141
Flag: getFlag(info),
141142
Size: info.Size(),
142143
Parent: dir,

analyze/dir_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ func TestAnalyzeDir(t *testing.T) {
1414
defer fin()
1515

1616
analyzer := CreateAnalyzer()
17-
dir := analyzer.AnalyzeDir("test_dir", func(_ string) bool { return false })
17+
dir := analyzer.AnalyzeDir("test_dir", func(_, _ string) bool { return false })
1818

1919
c := analyzer.GetProgressChan()
2020
progress := <-c
@@ -49,7 +49,7 @@ func TestIgnoreDir(t *testing.T) {
4949
fin := testdir.CreateTestDir()
5050
defer fin()
5151

52-
dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return true })
52+
dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return true })
5353

5454
assert.Equal(t, "test_dir", dir.Name)
5555
assert.Equal(t, 1, dir.ItemCount)
@@ -63,7 +63,7 @@ func TestFlags(t *testing.T) {
6363

6464
os.Symlink("test_dir/nested/file2", "test_dir/nested/file3")
6565

66-
dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return false })
66+
dir := CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return false })
6767
sort.Sort(dir.Files)
6868

6969
assert.Equal(t, int64(28+4096*4), dir.Size)
@@ -85,7 +85,7 @@ func TestHardlink(t *testing.T) {
8585

8686
os.Link("test_dir/nested/file2", "test_dir/nested/file3")
8787

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

9090
assert.Equal(t, int64(7+4096*3), dir.Size) // file2 and file3 are counted just once for size
9191
assert.Equal(t, 6, dir.ItemCount) // but twice for item count
@@ -103,7 +103,7 @@ func TestErr(t *testing.T) {
103103
os.Chmod("test_dir/nested", 0)
104104
defer os.Chmod("test_dir/nested", 0755)
105105

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

108108
assert.Equal(t, "test_dir", dir.GetName())
109109
assert.Equal(t, 2, dir.ItemCount)
@@ -119,5 +119,5 @@ func BenchmarkAnalyzeDir(b *testing.B) {
119119

120120
b.ResetTimer()
121121

122-
CreateAnalyzer().AnalyzeDir("test_dir", func(_ string) bool { return false })
122+
CreateAnalyzer().AnalyzeDir("test_dir", func(_, _ string) bool { return false })
123123
}

cmd/app/app.go

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"runtime"
99

10+
"github.com/dundee/gdu/v4/analyze"
1011
"github.com/dundee/gdu/v4/build"
1112
"github.com/dundee/gdu/v4/common"
1213
"github.com/dundee/gdu/v4/device"
@@ -16,18 +17,30 @@ import (
1617
"github.com/rivo/tview"
1718
)
1819

20+
// UI is common interface for both terminal UI and text output
21+
type UI interface {
22+
ListDevices(getter device.DevicesInfoGetter) error
23+
AnalyzePath(path string, parentDir *analyze.Dir) error
24+
SetIgnoreDirPaths(paths []string)
25+
SetIgnoreDirPatterns(paths []string) error
26+
SetIgnoreHidden(value bool)
27+
StartUILoop() error
28+
}
29+
1930
// Flags define flags accepted by Run
2031
type Flags struct {
21-
LogFile string
22-
IgnoreDirs []string
23-
MaxCores int
24-
ShowDisks bool
25-
ShowApparentSize bool
26-
ShowVersion bool
27-
NoColor bool
28-
NonInteractive bool
29-
NoProgress bool
30-
NoCross bool
32+
LogFile string
33+
IgnoreDirs []string
34+
IgnoreDirPatterns []string
35+
MaxCores int
36+
ShowDisks bool
37+
ShowApparentSize bool
38+
ShowVersion bool
39+
NoColor bool
40+
NonInteractive bool
41+
NoProgress bool
42+
NoCross bool
43+
NoHidden bool
3144
}
3245

3346
// App defines the main application
@@ -71,6 +84,17 @@ func (a *App) Run() error {
7184
}
7285

7386
ui.SetIgnoreDirPaths(a.Flags.IgnoreDirs)
87+
88+
if len(a.Flags.IgnoreDirPatterns) > 0 {
89+
if err := ui.SetIgnoreDirPatterns(a.Flags.IgnoreDirPatterns); err != nil {
90+
return err
91+
}
92+
}
93+
94+
if a.Flags.NoHidden {
95+
ui.SetIgnoreHidden(true)
96+
}
97+
7498
a.setMaxProcs()
7599

76100
if err := a.runAction(ui, path); err != nil {
@@ -91,8 +115,8 @@ func (a *App) setMaxProcs() {
91115
log.Printf("Max cores set to %d", runtime.GOMAXPROCS(0))
92116
}
93117

94-
func (a *App) createUI() common.UI {
95-
var ui common.UI
118+
func (a *App) createUI() UI {
119+
var ui UI
96120

97121
if a.Flags.NonInteractive || !a.Istty {
98122
ui = stdout.CreateStdoutUI(
@@ -124,7 +148,7 @@ func (a *App) setNoCross(path string) error {
124148
return nil
125149
}
126150

127-
func (a *App) runAction(ui common.UI, path string) error {
151+
func (a *App) runAction(ui UI, path string) error {
128152
if a.Flags.ShowDisks {
129153
if err := ui.ListDevices(a.Getter); err != nil {
130154
return fmt.Errorf("loading mount points: %w", err)

cmd/app/app_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,44 @@ func TestAnalyzePath(t *testing.T) {
5252
assert.Nil(t, err)
5353
}
5454

55+
func TestAnalyzePathWithIgnoring(t *testing.T) {
56+
fin := testdir.CreateTestDir()
57+
defer fin()
58+
59+
out, err := runApp(
60+
&Flags{
61+
LogFile: "/dev/null",
62+
IgnoreDirPatterns: []string{"/[abc]+"},
63+
NoHidden: true,
64+
},
65+
[]string{"test_dir"},
66+
false,
67+
testdev.DevicesInfoGetterMock{},
68+
)
69+
70+
assert.Contains(t, out, "nested")
71+
assert.Nil(t, err)
72+
}
73+
74+
func TestAnalyzePathWithIgnoringPatternError(t *testing.T) {
75+
fin := testdir.CreateTestDir()
76+
defer fin()
77+
78+
out, err := runApp(
79+
&Flags{
80+
LogFile: "/dev/null",
81+
IgnoreDirPatterns: []string{"[[["},
82+
NoHidden: true,
83+
},
84+
[]string{"test_dir"},
85+
false,
86+
testdev.DevicesInfoGetterMock{},
87+
)
88+
89+
assert.Equal(t, out, "")
90+
assert.NotNil(t, err)
91+
}
92+
5593
func TestAnalyzePathWithGui(t *testing.T) {
5694
fin := testdir.CreateTestDir()
5795
defer fin()

cmd/root.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,20 @@ func init() {
3232
af = &app.Flags{}
3333
flags := rootCmd.Flags()
3434
flags.StringVarP(&af.LogFile, "log-file", "l", "/dev/null", "Path to a logfile")
35-
flags.StringSliceVarP(&af.IgnoreDirs, "ignore-dirs", "i", []string{"/proc", "/dev", "/sys", "/run"}, "Absolute paths to ignore (separated by comma)")
3635
flags.IntVarP(&af.MaxCores, "max-cores", "m", runtime.NumCPU(), fmt.Sprintf("Set max cores that GDU will use. %d cores available", runtime.NumCPU()))
36+
flags.BoolVarP(&af.ShowVersion, "version", "v", false, "Print version")
37+
38+
flags.StringSliceVarP(&af.IgnoreDirs, "ignore-dirs", "i", []string{"/proc", "/dev", "/sys", "/run"}, "Absolute paths to ignore (separated by comma)")
39+
flags.StringSliceVarP(&af.IgnoreDirPatterns, "ignore-dirs-pattern", "I", []string{}, "Absolute path patterns to ignore (separated by comma)")
40+
flags.BoolVarP(&af.NoHidden, "no-hidden", "H", false, "Ignore hidden directories (beggining with dot)")
41+
flags.BoolVarP(&af.NoCross, "no-cross", "x", false, "Do not cross filesystem boundaries")
42+
3743
flags.BoolVarP(&af.ShowDisks, "show-disks", "d", false, "Show all mounted disks")
3844
flags.BoolVarP(&af.ShowApparentSize, "show-apparent-size", "a", false, "Show apparent size")
39-
flags.BoolVarP(&af.ShowVersion, "version", "v", false, "Print version")
4045
flags.BoolVarP(&af.NoColor, "no-color", "c", false, "Do not use colorized output")
4146
flags.BoolVarP(&af.NonInteractive, "non-interactive", "n", false, "Do not run in interactive mode")
4247
flags.BoolVarP(&af.NoProgress, "no-progress", "p", false, "Do not show progress in non-interactive mode")
43-
flags.BoolVarP(&af.NoCross, "no-cross", "x", false, "Do not cross filesystem boundaries")
48+
4449
}
4550

4651
func runE(command *cobra.Command, args []string) error {

common/ignore.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package common
2+
3+
import (
4+
"log"
5+
"regexp"
6+
"strings"
7+
8+
"github.com/dundee/gdu/v4/analyze"
9+
)
10+
11+
// CreateIgnorePattern creates one pattern from all path patterns
12+
func CreateIgnorePattern(paths []string) (*regexp.Regexp, error) {
13+
var err error
14+
15+
for i, path := range paths {
16+
if _, err = regexp.Compile(path); err != nil {
17+
return nil, err
18+
}
19+
paths[i] = "(" + path + ")"
20+
}
21+
22+
ignore := `^` + strings.Join(paths, "|") + `$`
23+
return regexp.Compile(ignore)
24+
}
25+
26+
// SetIgnoreDirPaths sets paths to ignore
27+
func (ui *UI) SetIgnoreDirPaths(paths []string) {
28+
log.Printf("Ignoring dirs %s", strings.Join(paths, ", "))
29+
ui.IgnoreDirPaths = make(map[string]struct{}, len(paths))
30+
for _, path := range paths {
31+
ui.IgnoreDirPaths[path] = struct{}{}
32+
}
33+
}
34+
35+
// SetIgnoreDirPatterns sets regular patters of dirs to ignore
36+
func (ui *UI) SetIgnoreDirPatterns(paths []string) error {
37+
var err error
38+
log.Printf("Ignoring dir patterns %s", strings.Join(paths, ", "))
39+
ui.IgnoreDirPathPatterns, err = CreateIgnorePattern(paths)
40+
return err
41+
}
42+
43+
// SetIgnoreHidden sets flags if hidden dirs should be ignored
44+
func (ui *UI) SetIgnoreHidden(value bool) {
45+
log.Printf("Ignoring hidden dirs")
46+
ui.IgnoreHidden = value
47+
}
48+
49+
// ShouldDirBeIgnored returns true if given path should be ignored
50+
func (ui *UI) ShouldDirBeIgnored(name, path string) bool {
51+
_, shouldIgnore := ui.IgnoreDirPaths[path]
52+
if shouldIgnore {
53+
log.Printf("Directory %s ignored", path)
54+
}
55+
return shouldIgnore
56+
}
57+
58+
// ShouldDirBeIgnoredUsingPattern returns true if given path should be ignored
59+
func (ui *UI) ShouldDirBeIgnoredUsingPattern(name, path string) bool {
60+
shouldIgnore := ui.IgnoreDirPathPatterns.MatchString(path)
61+
if shouldIgnore {
62+
log.Printf("Directory %s ignored", path)
63+
}
64+
return shouldIgnore
65+
}
66+
67+
// IsHiddenDir returns if the dir name beggins with dot
68+
func (ui *UI) IsHiddenDir(name, path string) bool {
69+
shouldIgnore := name[0] == '.'
70+
if shouldIgnore {
71+
log.Printf("Directory %s ignored", path)
72+
}
73+
return shouldIgnore
74+
}
75+
76+
// CreateIgnoreFunc returns function for detecting if dir should be ignored
77+
func (ui *UI) CreateIgnoreFunc() analyze.ShouldDirBeIgnored {
78+
switch {
79+
case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns == nil && !ui.IgnoreHidden:
80+
return ui.ShouldDirBeIgnored
81+
case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns != nil && !ui.IgnoreHidden:
82+
return func(name, path string) bool {
83+
return ui.ShouldDirBeIgnored(name, path) || ui.ShouldDirBeIgnoredUsingPattern(name, path)
84+
}
85+
case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns != nil && ui.IgnoreHidden:
86+
return func(name, path string) bool {
87+
return ui.ShouldDirBeIgnored(name, path) || ui.ShouldDirBeIgnoredUsingPattern(name, path) || ui.IsHiddenDir(name, path)
88+
}
89+
case len(ui.IgnoreDirPaths) == 0 && ui.IgnoreDirPathPatterns != nil && ui.IgnoreHidden:
90+
return func(name, path string) bool {
91+
return ui.ShouldDirBeIgnoredUsingPattern(name, path) || ui.IsHiddenDir(name, path)
92+
}
93+
case len(ui.IgnoreDirPaths) == 0 && ui.IgnoreDirPathPatterns != nil && !ui.IgnoreHidden:
94+
return ui.ShouldDirBeIgnoredUsingPattern
95+
case len(ui.IgnoreDirPaths) == 0 && ui.IgnoreDirPathPatterns == nil && ui.IgnoreHidden:
96+
return ui.IsHiddenDir
97+
case len(ui.IgnoreDirPaths) > 0 && ui.IgnoreDirPathPatterns == nil && ui.IgnoreHidden:
98+
return func(name, path string) bool {
99+
return ui.ShouldDirBeIgnored(name, path) || ui.IsHiddenDir(name, path)
100+
}
101+
default:
102+
return func(name, path string) bool { return false }
103+
}
104+
}

0 commit comments

Comments
 (0)