09
2024
05
10:58:46

Github每日精选(第44期):磁盘工具diskusage

diskusage

diskusage 是 显示磁盘使用情况的工具。同时支持(Linux、macOSWindows)三大系统。

虽然说现在有很多的磁盘使用情况的工具,diskusage 还是有他的独特之处,他使用的是go语言进行编写,代码量非常的小,非常适合我们的go语言的初学着,等下我们也会对主要的代码进行分析,清楚的知道这款工具的逻辑。

在这里插入图片描述

安装

diskusage 的安装也比较简单了,使用传统的go install 就能轻松的把diskusage 安装到本地。

go install github.com/chenquan/diskusage@latest
使用

先看看使用帮助,来显示出diskusage 所支持的命令和diskusage 的功能。diskusage -h

$ diskusage -h
A tool for showing disk usage.

Usage:
  diskusage [flags]Flags:
  -a, --all             display all directories, otherwise only display folders whose usage size is not 0
  -c, --color string    set color output mode. optional: auto, always, ignore (default "auto")
  -d, --depth int       shows the depth of the tree directory structure (default 1)
      --dir string      dir path (default "./")
  -f, --filter string   regular expression filter (default ".+")
  -h, --help            help for diskusage
  -t, --type strings    only count certain types of files  (default all)
  -u, --unit string     displayed units. optional: B(Bytes), K(KB), M(MB), G(GB), T(TB) (default "M")

最常见的一个功能就是列出当前目录的占用情况,经常我们在使用 linux的过程中,会产生一些文件,使得我们的磁盘爆满,但是我们又不知道占用最大空间的文件它在哪里,可以使用diskusage -a的命令来显示目录的占用情况。

diskusage  -a

显示当前目录的情况如下:

Total: 3114.888M	/root
--------------------------------- 1692.0M  54.3% ┌─ snap 1284.5M  41.2% ├─ .cache  100.2M   3.2% ├─ .EasyOCR   31.0M   1.0% ├─ go    4.3M   0.1% ├─ flutter-webrtc-demo    2.1M   0.1% ├─ .launchpadlib    0.4M   0.0% ├─ 1yw32lok3j51yw32lok3j5.jpg    0.2M   0.0% ├─ 1yw32lok3j51yw32lok3j5-1.jpg   54.1K   0.0% ├─ 4f70c06988d732947b62c18501a876a8-1.jpeg   43.6K   0.0% ├─ 4f70c06988d732947b62c18501a876a8.jpeg   22.5K   0.0% ├─ Dingtalk_20220804094819.jpg   14.7K   0.0% ├─ .viminfo    3.7K   0.0% ├─ .bashrc    0.6K   0.0% ├─ .bash_history    0.5K   0.0% ├─ scene.py    0.2K   0.0% ├─ .python_history    0.2K   0.0% ├─ .profile    0.1K   0.0% ├─ .config   78.0B   0.0% ├─ .flutter    0.0B   0.0% ├─ .jina    0.0B   0.0% └─ .sudo_as_admin_successful

这个就非常清楚的展示了目录的情况,当前目录大小3114.888M,snap目录占了1692.0M 占了 54.3%,目录的情况一目了然,还怕找不到相关的文件吗?

代码分析

diskusage 的文件结构也比较简单,最重要的文件为stat.go,有几个函数比较关键,接下来一个一个分析下,也比较简单,相当于教我们怎么 统计文件夹的情况。
在这里插入图片描述
主函数在这里,看起来逻辑也挺正常的,先算总的文件大小,在一个一个的去遍历:

func Stat(cmd *cobra.Command, _ []string) error {
	flags := cmd.Flags()
	dir, err := flags.GetString("dir")
	if err != nil {
		return err	}

	depth, err := flags.GetInt("depth")
	if err != nil {
		return err	}

	unit, err := getUnit(flags)
	if err != nil {
		return err	}

	dir, err = filepath.Abs(dir)
	if err != nil {
		return err	}

	types, err := flags.GetStringSlice("type")
	if err != nil {
		return err	}
	typeMap := make(map[string]struct{}, len(types))
	for _, s := range types {
		typeMap["."+s] = struct{}{}
	}

	filter, err := flags.GetString("filter")
	if err != nil {
		return err	}
	compile, err := regexp.Compile(filter)
	if err != nil {
		return err	}

	all, err := flags.GetBool("all")
	if err != nil {
		return err	}

	err = handleColor(flags)
	if err != nil {
		return err	}

	go func() {
		defer close(errChan)

		files, err := find(dir, func(info fs.FileInfo) bool {
			if info.IsDir() {
				return true
			}

			name := info.Name()
			ext := filepath.Ext(name)
			_, ok := typeMap[ext]
			typeB := ok || len(types) == 0

			filterB := compile.MatchString(name)

			return typeB && filterB		})
		if err != nil {
			errChan <- err			return
		}

		totalSize := int64(0)
		for _, f := range files {
			totalSize += f.size		}

		val, reduceUnit := getReduce(unit, totalSize)
		header := fmt.Sprintf("Total: %0.3f%s\t%s", val, reduceUnit, color.HiGreenString(dir))
		colorPrintln(header)
		colorPrintln(strings.Repeat("-", len(header)+2))

		l := list.NewWriter()
		l.SetStyle(list.StyleConnectedLight)

		infoFiles := buildInfoFile(l, files, 0, depth, unit, totalSize, all)
		maxLen := 0
		for _, info := range infoFiles {
			if maxLen < len(info.str) {
				maxLen = len(info.str)
			}
		}
		printTree(l.Render(), infoFiles, maxLen)

		errChan <- nil
	}()

	if <-errChan != nil {
		return err	}

	return nil}

文件大小格式化:

func getReduce(unit string, n int64) (float64, string) {
	reduce := 0
	switch unit {
	case "B":
		reduce = 0
	case "K":
		reduce = 1
	case "M":
		reduce = 2
	case "G":
		reduce = 3
	case "T":
		reduce = 4
	}
	for {
		if reduce <= 0 {
			break
		}

		if int(float64(n)/float64(units[reduce])*10) > 0 {
			break
		}

		reduce--

	}

	return float64(n) / float64(units[reduce]), unitStrings[reduce]}

查找文件:

func find(dir string, filter func(info fs.FileInfo) bool) ([]file, error) {
	if !sysFilter(dir) {
		return nil, nil
	}

	dirEntries, err := os.ReadDir(dir)
	if err != nil {
		if pathError, ok := err.(*fs.PathError); ok {
			// currently only supports Windows
			if accessDeniedSyscall(pathError.Err) {
				return nil, errorAccessDenied			}
		}
		return nil, err	}

	var wg = sync.WaitGroup{}
	fileChan := make(chan file, len(dirEntries))
	for _, entry := range dirEntries {
		entry := entry
		fileInfo, err := entry.Info()
		if err != nil {
			return nil, err		}

		if !filter(fileInfo) {
			continue
		}

		if !entry.IsDir() {
			fileChan <- file{
				name: entry.Name(),
				size: fileInfo.Size(),
			}
			continue
		}

		wg.Add(1)
		do := func() {
			defer wg.Done()

			subFiles, err := find(path.Join(dir, entry.Name()), filter)
			if err != nil {
				if accessDenied(err) {
					return
				}
				errChan <- err			}

			totalSize := int64(0)
			for _, subFile := range subFiles {
				totalSize += subFile.size			}

			fileChan <- file{
				sub:   subFiles,
				name:  entry.Name(),
				isDir: true,
				size:  totalSize,
			}
		}
		w.Run(do)
	}
	wg.Wait()
	close(fileChan)

	files := make([]file, 0, len(dirEntries))
	for f := range fileChan {
		files = append(files, f)
	}
	sort.Slice(files, func(i, j int) bool { return files[i].size > files[j].size })

	return files, nil}

递归遍历文件信息:

func buildInfoFile(l list.Writer, files []file, n, depth int, unit string, totalSize int64, all bool) []infoFile {
	if n == depth {
		return nil
	}

	var infoFiles []infoFile	for _, f := range files {
		if f.isDir && f.size == 0 && !all {
			continue
		}

		val, reduceUnit := getReduce(unit, f.size)
		infoFiles = append(infoFiles, infoFile{
			size:      val,
			uint:      reduceUnit,
			usageRate: float64(f.size) / float64(totalSize) * 100,
			str:       fmt.Sprintf("%0.1f", val),
			isDir:     f.isDir,
		})

		name := f.name		if f.isDir {
			name = color.HiGreenString(name)
		}
		l.AppendItem(name)

		if f.isDir {
			l.Indent()
			subUsageSizes := buildInfoFile(l, f.sub, n+1, depth, unit, totalSize, all)
			infoFiles = append(infoFiles, subUsageSizes...)
			l.UnIndent()
		}
	}

	return infoFiles}

把结果显示出来:

func printTree(content string, infoFiles []infoFile, maxLen int) {
	size := len(infoFiles)
	format := " %" + strconv.Itoa(maxLen) + ".1f%s %5.1f%%"
	for i, line := range strings.Split(content, "\n") {
		if i >= size {
			continue
		}

		info := infoFiles[i]
		str := fmt.Sprintf(format, info.size, info.uint, info.usageRate)
		if info.isDir {
			str = color.HiRedString(str)
		}

		colorPrintln(str, line)
	}




推荐本站淘宝优惠价购买喜欢的宝贝:

image.png

本文链接:https://hqyman.cn/post/5981.html 非本站原创文章欢迎转载,原创文章需保留本站地址!

分享到:
打赏





休息一下~~


« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

请先 登录 再评论,若不是会员请先 注册

您的IP地址是: