網站首頁 編程語言 正文
需求
寫一個簡版類似于unix tree命令的go語言小程序,如下參數仿照于tree命令的文檔
該小程序支持的功能如下:
mtree命令默認打印以層級結構打印所有目錄和文件,默認以字母排序,支持以下參數
mtree [-adfLopugshDtr]
列出相關參數
-d ?僅列出目錄
-L ?指定需要打印目錄結構的層數
-o filename 將結果輸出到指定文件
文件相關參數
-f ?打印每個文件基于當前位置的全路徑
-p 打印每個文件的權限
-u 打印屬主名稱
-g 打印屬組名稱
-s 打印文件大小
-h 打印文件大小,以更加人性化的方式,會帶上單位,比如 K、M、G、T
-D 打印文件最后的修改時間
排序相關參數
-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:
│ │ │ └──
代碼思路:針對程序當前執行路徑,遍歷子目錄節點,如果子節點同樣是目錄的話,則遞歸遍歷;在遞歸遍歷的過程中,獲取每一個文件的相關屬性信息; 同時,對于程序指定的參數,可以分為屬性類參數和控制類參數
- 屬性類參數有: -u -g -f -p -s -h
- 控制類參數有: -L -o -d -D -t
對于屬性類的參數,定義不同的filter,在遍歷的過程中,每一個文件節點,都經過過濾器過濾,用于填充對應的屬性信息; 對于控制類的參數,則貫穿于程序過程中,用于控制不同的行為
這里重點分析下如何輸出前綴的這種層級關系的前綴,以下述目錄結構進行舉例說明:
目錄結構
. └── workspace ├── cppProj │ └── dockerExecDemo ├── k8s │ └── _05 │ ├── _15 │ ├── _17 │ └── _18 └── podDemo
眾所周知,unix文件系統是樹狀結構;因此,這里首先就目錄結構抽象成樹結構,如下圖,左上角是對應的目錄結構,右邊是抽象的樹狀結構:
圖豎著看可能還不夠清晰,把該樹結構橫過來,這樣就可以很好的用該樹結構和目錄的層級關系進行一一類比了
下面重點分析下如何輸出每一層目錄的前綴,先看└──
和├──
是如何確定的,如上目錄結構所示, _18
節點屬于目錄_05
的最后一個節點,因此_18
節點前面為字符串└──
; 一個目錄中,除了最后一個節點前面的字符串為└──
,其他節點前面的字符串都是├──
,這里的前綴字符串很容易就可以總結得出; 但是對于節點dockerExecDemo
前面的字符串 │
, 和_18
節點前面的字符串 │
,該如何打印呢? 通過分析我們的樹狀結構可以總結得出,如果一個節點的父親沒有右兄弟,則添加前綴
, 否則添加前綴│
, 每一個節點都以這樣的方式追隨到根結點,就可以得出該節點的前綴關系;這里已_15
節點來完整的舉例說明:
-
_15
節點屬于第一個節點,因此首先添加前綴 "├──"; 前綴結果為: "├──" -
_15
節點的父親_05
沒有右兄弟,因此繼續添加前綴 " "; 前綴結果為:" ├──" -
_05
節點的父親k8s
有右兄弟,因此繼續添加前綴 "│ "; 前綴結果為:"│ ├──" -
k8s
節點的父親workspace
沒有右兄弟,因此繼續添加前綴 " "; 前綴結果為:" │ ├──" - 最終
_15
節點的整體前綴為 " │ ├── "
上面所述的樹結構,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,同時每一文件節點,都經過參數過濾器進行格式化輸出 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(), } // 維護左右兄弟節點關系 if pre != nil { pre.Right = childMFile } pre = childMFile info, err := dir.Info() if err != nil { return err } // 參數過濾器 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 }
最終程序實現的輸出和 unix tree命令輸出對比如下圖所示,幾乎完整的實現了自己的目的:
原文鏈接:https://juejin.cn/post/7158826961345708062
相關推薦
- 2023-07-06 css flex實現div固定在瀏覽器右下角
- 2022-11-30 詳解Golang中字符串的使用_Golang
- 2023-07-16 callBack: function(res){} 與 callBack: res =>{}
- 2023-05-21 使用react完成點擊返回頂部操作_React
- 2022-12-10 關于torch中tensor數據類型的轉換_python
- 2022-09-04 C++實現ETW進行進程變動監控詳解_C 語言
- 2022-05-31 Pandas保存csv數據的三種方式詳解_python
- 2022-04-07 C++11實現字符串分割的示例_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支