基于Go語言實現(xiàn)類似tree命令的小程序
需求
寫一個簡版類似于unix tree命令的go語言小程序,如下參數(shù)仿照于tree命令的文檔
該小程序支持的功能如下:
mtree命令默認打印以層級結構打印所有目錄和文件,默認以字母排序,支持以下參數(shù)
mtree [-adfLopugshDtr]
列出相關參數(shù)
-d 僅列出目錄
-L 指定需要打印目錄結構的層數(shù)
-o filename 將結果輸出到指定文件
文件相關參數(shù)
-f 打印每個文件基于當前位置的全路徑
-p 打印每個文件的權限
-u 打印屬主名稱
-g 打印屬組名稱
-s 打印文件大小
-h 打印文件大小,以更加人性化的方式,會帶上單位,比如 K、M、G、T
-D 打印文件最后的修改時間
排序相關參數(shù)
-t 以最后修改時間排序輸出
eg:
命令: ./main -u -g -p -D -s -o tmp.txt
tmp.txt文件內容如下
. ├── [-rwxr-xr-x root root 2138809 Oct 26 2022] main ├── [-rw-r--r-- root root 2009 Oct 25 2022] tmp.txt └── [drwxr-xr-x root root 4096 Mar 14 2022] workspace ├── [drwxr-xr-x root root 4096 Mar 14 2022] cppProj │ ├── [drwxr-xr-x root root 4096 Mar 14 2022] dockerExecDemo │ ├── [-rw-r--r-- root root 398 Mar 14 2022] exec.cpp │ └── [-rwxr-xr-x root root 16872 Mar 14 2022] set_ns ├── [drwxr-xr-x root root 4096 Mar 16 2022] k8s │ ├── [drwxr-xr-x root root 4096 Mar 17 2022] _05 │ │ ├── [drwxr-xr-x root root 4096 Mar 16 2022] _15 │ │ │ ├── [-rw-r--r-- root root 10 Mar 16 2022] password.txt │ │ │ ├── [-rw-r--r-- root root 405 Mar 16 2022] test_projected_volumn.yml │ │ │ └── [-rw-r--r-- root root 7 Mar 16 2022] username.txt │ │ ├── [drwxr-xr-x root root 4096 Mar 17 2022] _17 │ │ │ └── [-rw-r--r-- root root 340 Mar 16 2022] nginx-deployment.yaml │ │ └── [drwxr-xr-x root root 4096 Mar 17 2022] _18 │ │ ├── [-rw-r--r-- root root 165 Mar 17 2022] srv.yml │ │ └── [-rw-r--r-- root root 346 Mar 17 2022] statefulset.yaml │ ├── [-rwxrwxrwx root root 313 Jan 26 2022] nginx.yaml │ ├── [-rw-r--r-- root root 197 Mar 16 2022] nginx2.yml │ └── [-rw-r--r-- root root 188 Jan 26 2022] nginx_service.yaml └── [drwxr-xr-x root root 4096 Jan 28 2022] podDemo └── [-rw-r--r-- root root 201 Jan 28 2022] nginx.yaml
目的
在個人mac電腦上,想在終端里面看看目錄結構,奈何網絡條件不好,tree命令沒有安裝成功,于是動手自己寫一個tree命令的想法油然而生; 同時又可以熟悉go語言標準庫,提高編碼能力,本程序主要涉及go pkg有:flag、os、fs、path、filepath等
需求分析
針對上面的輸出顯示,可以把每一行的信息分為
- 文件路徑,文件名稱,文件的相對路徑, eg:
./workspace/k8s/_05/_15/password.txt
- 文件屬性,權限,屬主,屬組,大小,時間 eg:
[root root 10]
- 前綴 eg:
│ │ │ └──
代碼思路:針對程序當前執(zhí)行路徑,遍歷子目錄節(jié)點,如果子節(jié)點同樣是目錄的話,則遞歸遍歷;在遞歸遍歷的過程中,獲取每一個文件的相關屬性信息; 同時,對于程序指定的參數(shù),可以分為屬性類參數(shù)和控制類參數(shù)
- 屬性類參數(shù)有: -u -g -f -p -s -h
- 控制類參數(shù)有: -L -o -d -D -t
對于屬性類的參數(shù),定義不同的filter,在遍歷的過程中,每一個文件節(jié)點,都經過過濾器過濾,用于填充對應的屬性信息; 對于控制類的參數(shù),則貫穿于程序過程中,用于控制不同的行為
這里重點分析下如何輸出前綴的這種層級關系的前綴,以下述目錄結構進行舉例說明:
目錄結構
. └── workspace ├── cppProj │ └── dockerExecDemo ├── k8s │ └── _05 │ ├── _15 │ ├── _17 │ └── _18 └── podDemo
眾所周知,unix文件系統(tǒng)是樹狀結構;因此,這里首先就目錄結構抽象成樹結構,如下圖,左上角是對應的目錄結構,右邊是抽象的樹狀結構:
圖豎著看可能還不夠清晰,把該樹結構橫過來,這樣就可以很好的用該樹結構和目錄的層級關系進行一一類比了
下面重點分析下如何輸出每一層目錄的前綴,先看└──
和├──
是如何確定的,如上目錄結構所示, _18
節(jié)點屬于目錄_05
的最后一個節(jié)點,因此_18
節(jié)點前面為字符串└──
; 一個目錄中,除了最后一個節(jié)點前面的字符串為└──
,其他節(jié)點前面的字符串都是├──
,這里的前綴字符串很容易就可以總結得出; 但是對于節(jié)點dockerExecDemo
前面的字符串 │
, 和_18
節(jié)點前面的字符串 │
,該如何打印呢? 通過分析我們的樹狀結構可以總結得出,如果一個節(jié)點的父親沒有右兄弟,則添加前綴
, 否則添加前綴│
, 每一個節(jié)點都以這樣的方式追隨到根結點,就可以得出該節(jié)點的前綴關系;這里已_15
節(jié)點來完整的舉例說明:
_15
節(jié)點屬于第一個節(jié)點,因此首先添加前綴 "├──"; 前綴結果為: "├──"_15
節(jié)點的父親_05
沒有右兄弟,因此繼續(xù)添加前綴 " "; 前綴結果為:" ├──"_05
節(jié)點的父親k8s
有右兄弟,因此繼續(xù)添加前綴 "│ "; 前綴結果為:"│ ├──"k8s
節(jié)點的父親workspace
沒有右兄弟,因此繼續(xù)添加前綴 " "; 前綴結果為:" │ ├──"- 最終
_15
節(jié)點的整體前綴為 " │ ├── "
上面所述的樹結構,go語言的定義如下:
type MFileNode struct { Level int ModTime time.Time UserName string Size int64 GroupName string FileName string IsDir bool Children []*MFileNode Parent *MFileNode Left *MFileNode Right *MFileNode Prefix strings.Builder Attr strings.Builder Path strings.Builder }
核心遍歷目錄樹的代碼如下,
// Walk 文件結構遍歷目錄樹,構造MTree,同時每一文件節(jié)點,都經過參數(shù)過濾器進行格式化輸出 func Walk(parentMFile *MFileNode, parent, base string, filters *MFilter) error { if parentMFile.Level >= level { return nil } dirEntries, err := os.ReadDir(filepath.Join(parent, base)) if err != nil { return err } if len(dirEntries) == 0 { return nil } parentMFile.Children = make([]*MFileNode, 0, len(dirEntries)) var pre *MFileNode = nil for _, dir := range dirEntries { if onlyDir && !dir.IsDir() { continue } childMFile := &MFileNode{ Level: parentMFile.Level + 1, Children: nil, FileName: dir.Name(), Parent: parentMFile, Left: pre, IsDir: dir.IsDir(), } // 維護左右兄弟節(jié)點關系 if pre != nil { pre.Right = childMFile } pre = childMFile info, err := dir.Info() if err != nil { return err } // 參數(shù)過濾器 filters.Exec(childMFile, info) parentMFile.Children = append(parentMFile.Children, childMFile) if dir.IsDir() { err1 := Walk(childMFile, filepath.Join(parent, base), dir.Name(), filters) if err1 != nil { return err1 } } } return nil }
最終程序實現(xiàn)的輸出和 unix tree命令輸出對比如下圖所示,幾乎完整的實現(xiàn)了自己的目的:
到此這篇關于基于Go語言實現(xiàn)類似tree命令的小程序的文章就介紹到這了,更多相關Go語言 tree命令內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
GO語言中創(chuàng)建切片的三種實現(xiàn)方式
這篇文章主要介紹了GO語言中創(chuàng)建切片的三種實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09