使用Golang获取目录磁盘使用情况

huangapple go评论86阅读模式
英文:

Get Directory Disk Usage with Golang

问题

我尝试使用Go语言学习目录的磁盘使用情况,但是下面的应用程序计算结果不正确。我不想使用filepath.Walk。我尝试了os.Stat(),但是没有得到实际的目录大小。我在下面分享了相关的代码语句、正确的目录信息以及应用程序的结果。

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
)

type DiskStatus struct {
	All  uint64
	Free uint64
	Used uint64
}

func DiskUsage(path string) (ds DiskStatus) {
	fs := syscall.Statfs_t{}
	err := syscall.Statfs(path, &fs)
	if err != nil {
		return
	}
	ds.All = fs.Blocks * uint64(fs.Bsize)
	ds.Free = fs.Bfree * uint64(fs.Bsize)
	ds.Used = ds.All - ds.Free
	fmt.Println(path, ds.Used)
	return ds
}

func GetDir(destination_directory string) *[]model.Directory {
	var dir []model.Directory
	items, err := ioutil.ReadDir(destination_directory)

	if err != nil {
		log.Fatalln(err)
	}

	for _, item := range items {
		size := DiskUsage(destination_directory+item.Name()).Used / GB
		item := model.Directory{Path: item.Name(), TotalSize: size}
		dir = append(dir, item)
	}
	return &dir
}

正确的目录信息:

$du -sh *
8.0K containers
2.0G testfolder
3.2M workspace

应用程序的结果:

containers --> 90
testfolder --> 90
workspace --> 90
英文:

I try to learn disk usage of directory with golang but this below app calculate incorrect. I do not want to use filepath.Walk. I tried os.Stat() but that did not get actual directory size. I shared related code statements below, correct directory information and results of app.

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
)

type DiskStatus struct {
	All  uint64
	Free uint64
	Used uint64
}

func DiskUsage(path string) (ds DiskStatus) {
	fs := syscall.Statfs_t{}
	err := syscall.Statfs(path, &fs)
	if err != nil {
		return
	}
	ds.All = fs.Blocks * uint64(fs.Bsize)
	ds.Free = fs.Bfree * uint64(fs.Bsize)
	ds.Used = ds.All - ds.Free
	fmt.Println(path, ds.Used)
	return ds
}

func GetDir(destination_directory string) *[]model.Directory {
	var dir []model.Directory
	items, err := ioutil.ReadDir(destination_directory)

	if err != nil {
		log.Fatalln(err)
	}

	for _, item := range items {
		size := DiskUsage(destination_directory+item.Name()).Used / GB
		item := model.Directory{Path: item.Name(), TotalSize: size}
		dir = append(dir, item)
	}
	return &dir
}

Correct directory info:

$du -sh *
8.0K containers
2.0G testfolder
3.2M workspace

Results by App:

containers --> 90
testfolder --> 90
workspace --> 90

答案1

得分: 4

我认为你误用了syscall.Statfs函数:它返回的是文件系统统计信息,而不是单个目录或文件的大小。因此,你看到的输出并不是单个目录的大小,而是这些目录所在文件系统的整体磁盘使用情况。

如果你不想使用filepath.Walk(实际上这是一种通过递归遍历所有文件来计算目录大小的好方法),你可以编写自己的递归函数

package main

import (
	"os"
	"log"
	"fmt"
)

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
)

func DirSize(path string) (int64, error) {
	var size int64
	entries, err := os.ReadDir(path)
	if err != nil {
		return size, err
	}
	for _, entry := range entries {
		if entry.IsDir() {
			subDirSize, err := DirSize(path + "/" + entry.Name())
			if err != nil {
				log.Printf("failed to calculate size of directory %s: %v\n", entry.Name(), err)
				continue
			}
			size += subDirSize
		} else {
			fileInfo, err := entry.Info()
			if err != nil {
				log.Printf("failed to get info of file %s: %v\n", entry.Name(), err)
				continue
			}
			size += fileInfo.Size()
		}
	}
	return size, nil
}

func main() {
	dirs := []string{"./containers", "./testfolder", "./workspace"}

	for _, dir := range dirs {
		size, err := DirSize(dir)
		if err != nil {
			log.Printf("failed to calculate size of directory %s: %v\n", dir, err)
			continue
		}
		fmt.Printf("%s --> %.2f GB\n", dir, float64(size)/float64(GB))
	}
}

这段代码将遍历给定目录下的每个目录和文件,并累加它们的大小。如果条目是目录,它将递归计算目录的大小。然而,这段代码将不会处理符号链接或其他特殊文件。

另一种选择是DirSize函数使用filepath.Walk来遍历指定目录下的所有文件,包括子目录。filepath.Walk函数接受两个参数:根目录和一个在以根为根的树中的每个文件或目录上调用的函数。

在这里,对于它遇到的每个文件,它将文件的大小添加到总大小中(忽略目录)。由于filepath.Walk会自动处理递归遍历,所以通常比手动编写递归函数更简单直接。(playground)

package main

import (
	"os"
	"log"
	"fmt"
	"path/filepath"
)

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
)

func DirSize(path string) (int64, error) {
	var size int64
	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			size += info.Size()
		}
		return err
	})
	return size, err
}

func main() {
	dirs := []string{"./containers", "./testfolder", "./workspace"}

	for _, dir := range dirs {
		size, err := DirSize(dir)
		if err != nil {
			log.Printf("failed to calculate size of directory %s: %v\n", dir, err)
			continue
		}
		fmt.Printf("%s --> %.2f GB\n", dir, float64(size)/float64(GB))
	}
}
英文:

I believe you are misusing the syscall.Statfs function: it returns file system statistics, not the size of individual directories or files. So, what you are seeing as output is not the size of individual directories, but the overall disk usage for the filesystem that these directories reside on.

If you do not want to use filepath.Walk (which is actually a good way to calculate the directory size by recursively going through all the files), you can write your own recursive function.

package main

import (
	"os"
	"log"
	"fmt"
)

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
)

func DirSize(path string) (int64, error) {
	var size int64
	entries, err := os.ReadDir(path)
	if err != nil {
		return size, err
	}
	for _, entry := range entries {
		if entry.IsDir() {
			subDirSize, err := DirSize(path + "/" + entry.Name())
			if err != nil {
				log.Printf("failed to calculate size of directory %s: %v\n", entry.Name(), err)
				continue
			}
			size += subDirSize
		} else {
			fileInfo, err := entry.Info()
			if err != nil {
				log.Printf("failed to get info of file %s: %v\n", entry.Name(), err)
				continue
			}
			size += fileInfo.Size()
		}
	}
	return size, nil
}

func main() {
	dirs := []string{"./containers", "./testfolder", "./workspace"}

	for _, dir := range dirs {
		size, err := DirSize(dir)
		if err != nil {
			log.Printf("failed to calculate size of directory %s: %v\n", dir, err)
			continue
		}
		fmt.Printf("%s --> %.2f GB\n", dir, float64(size)/float64(GB))
	}
}

That would go through each directory and file under the given directory and sum up the sizes.
If the entry is a directory, it will recursively calculate the size of the directory.
Although... this code will not handle symbolic links or other special files.


The other option is for the DirSize function uses filepath.Walk to traverse all files under the specified directory, including subdirectories.
The filepath.Walk function takes two arguments: the root directory and a function that will be called for each file or directory in the tree rooted at the root.

Here, for each file it encounters, it adds the size of the file to the total size (ignoring directories).
Since filepath.Walk automatically handles recursive traversal for you, it is generally simpler and more straightforward than manually writing the recursive function.
(playground)

package main

import (
	"os"
	"log"
	"fmt"
	"path/filepath"
)

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
)

func DirSize(path string) (int64, error) {
	var size int64
	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			size += info.Size()
		}
		return err
	})
	return size, err
}

func main() {
	dirs := []string{"./containers", "./testfolder", "./workspace"}

	for _, dir := range dirs {
		size, err := DirSize(dir)
		if err != nil {
			log.Printf("failed to calculate size of directory %s: %v\n", dir, err)
			continue
		}
		fmt.Printf("%s --> %.2f GB\n", dir, float64(size)/float64(GB))
	}
}

huangapple
  • 本文由 发表于 2023年7月18日 16:09:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76710713.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定