前幾天寫筆記的時候,想要一個目錄樹,很無奈手上沒有任何工具,只能本身照着目錄結構一個一個敲。今天就索性本身動手,用go結合alfred寫一個打印目錄樹到粘貼板的workflow,這是演示圖git
|────README.md
|────go.mod
|────go.sum
|────tree.go
|────|image
|────────example.gif
|────|response
|────────icon.go
|────────info.go
|────────response.go
|────|workflow
|────────打印目錄樹.alfredworkflow
複製代碼
要打印目錄樹,最最基礎的就是對目錄進行操做,下面開始敲代碼吧github
先看看這個函數的五個參數json
func traverse(infos []os.FileInfo, lastDirPos, deep int, currentPath string, tree *string) {
//打印的前綴
prefix := "|"
//當前目錄下子目錄以及文件的總數
length := len(infos)
//首先打印出文件的名稱
for j := lastDirPos + 1; j < length; j++ {
// "."開頭的文件在MacOS表示隱藏文件,這裏我不想打印出來
if strings.Index(infos[j].Name(), ".") == 0 {
continue
}
//經過打印函數,把打印結果添加到tree變量裏
*tree += printName(infos[j], prefix, deep, FILE)
}
//而後打印目錄的名稱
for i := 0; i <= lastDirPos; i++ {
//同前面的文件打印,過濾掉隱藏目錄
if strings.Index(infos[i].Name(), ".") == 0 {
continue
}
//當前路徑+當前目錄,構成下一級遍歷的路徑
dirPath := currentPath + "/" + infos[i].Name()
//讀取下一級路徑中包含的文件和子目錄,得到集合
files, _ := ioutil.ReadDir(dirPath)
//若是下一級路徑中沒有文件和子目錄,打印次目錄名開始下一次循環
if len(files) == 0 {
*tree += printName(infos[i], prefix, deep, DIR)
continue
}
//對讀出來的集合進行排序操做
lastDirPosC := sortFile(&files)
//打印目錄名
*tree += printName(infos[i], prefix, deep, DIR)
//代碼若是走到這裏,則說明目錄下還有子目錄或者文件,進行遞歸遍歷
traverse(files, lastDirPosC, deep+1, dirPath, tree)
}
}
複製代碼
這個函數就是用來遞歸遍歷目錄的,它有五個參數數組
該方法的運行過程是bash
func sortFile(infos *[]os.FileInfo) int {
lastDirPos := len(*infos) - 1
adjustPos(*infos, &lastDirPos)
for i := 0; i < lastDirPos; i++ {
if !(*infos)[i].IsDir() {
swap(*infos, i, lastDirPos)
adjustPos(*infos, &lastDirPos)
}
}
dirSlice := (*infos)[:lastDirPos+1]
fileSlice := (*infos)[lastDirPos+1:]
sort.Slice(dirSlice, func(i, j int) bool { return dirSlice[i].Name() < dirSlice[j].Name() })
sort.Slice(fileSlice, func(i, j int) bool { return fileSlice[i].Name() < fileSlice[j].Name() })
merge := append(dirSlice, fileSlice...)
infos = &merge
return lastDirPos
}
複製代碼
在目錄與文件分類邏輯部分,用到兩個指針,i和lastDirPos數據結構
func adjustPos(infos []os.FileInfo, lastDirPos *int) {
for !infos[*lastDirPos].IsDir() {
*lastDirPos--
if *lastDirPos == -1 {
break
}
}
}
複製代碼
該函數就是對infos數組進行從右往左遍歷,找到最右邊的目錄對象app
*lastDirPos--
複製代碼
繼續循環,直到對象是目錄時終止函數
我首先定義了兩個常量,分別表示目錄類型和文件類型工具
const (
DIR = iota
FILE
)
複製代碼
下面看打印函數ui
func printName(file os.FileInfo, prefix string, deep int, fileType int) string {
var placeHolder string
switch fileType {
case DIR:
placeHolder = strings.Repeat("────", deep) + "|"
break
case FILE:
placeHolder = strings.Repeat("────", deep)
}
return fmt.Sprintln(prefix + placeHolder + file.Name())
}
複製代碼
它有四個參數對象
其邏輯是這樣的:
前面幾個就是打印目錄的核心函數,下面是完整的代碼
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"print/response"
"sort"
"strings"
)
const (
DIR = iota
FILE
)
func main() {
args := os.Args
if args == nil || len(args) < 2 {
Usage()
return
}
currentPath := args[1]
files, _ := ioutil.ReadDir(currentPath)
lastDirPos := sortFile(&files)
deep := 1
tree := ""
traverse(files, lastDirPos, deep, currentPath, &tree)
fmt.Println(tree)
}
func traverse(infos []os.FileInfo, lastDirPos, deep int, currentPath string, tree *string) {
prefix := "|"
length := len(infos)
for j := lastDirPos + 1; j < length; j++ {
if strings.Index(infos[j].Name(), ".") == 0 {
continue
}
*tree += printName(infos[j], prefix, deep, FILE)
}
for i := 0; i <= lastDirPos; i++ {
if strings.Index(infos[i].Name(), ".") == 0 {
continue
}
dirPath := currentPath + "/" + infos[i].Name()
files, _ := ioutil.ReadDir(dirPath)
if len(files) == 0 {
*tree += printName(infos[i], prefix, deep, DIR)
continue
}
lastDirPosC := sortFile(&files)
*tree += printName(infos[i], prefix, deep, DIR)
traverse(files, lastDirPosC, deep+1, dirPath, tree)
}
}
func printName(file os.FileInfo, prefix string, deep int, fileType int) string {
var placeHolder string
switch fileType {
case DIR:
placeHolder = strings.Repeat("────", deep) + "|"
break
case FILE:
placeHolder = strings.Repeat("────", deep)
}
return fmt.Sprintln(prefix + placeHolder + file.Name())
}
func sortFile(infos *[]os.FileInfo) int {
lastDirPos := len(*infos) - 1
adjustPos(*infos, &lastDirPos)
for i := 0; i < lastDirPos; i++ {
if !(*infos)[i].IsDir() {
swap(*infos, i, lastDirPos)
adjustPos(*infos, &lastDirPos)
}
}
dirSlice := (*infos)[:lastDirPos+1]
fileSlice := (*infos)[lastDirPos+1:]
sort.Slice(dirSlice, func(i, j int) bool { return dirSlice[i].Name() < dirSlice[j].Name() })
sort.Slice(fileSlice, func(i, j int) bool { return fileSlice[i].Name() < fileSlice[j].Name() })
merge := append(dirSlice, fileSlice...)
infos = &merge
return lastDirPos
}
func swap(infos []os.FileInfo, i, j int) {
temp := infos[i]
infos[i] = infos[j]
infos[j] = temp
}
func adjustPos(infos []os.FileInfo, lastDirPos *int) {
for !infos[*lastDirPos].IsDir() {
*lastDirPos--
if *lastDirPos == -1 {
break
}
}
}
var Usage = func() {
fmt.Println("input param")
}
複製代碼
到此,就能夠直接在命令行中輸出一個樹形結構的目錄,下面介紹如何經過alfred的workflow將結果複製到粘貼板
用過alfred的都熟悉這個界面
下面對這五個部分設置進行說明
列表有以下的數據結構,它以json表示:
{
"items":[
{
"uid":"8A9673FB-8CB1-BD04-AB30-3A8D820E5727",
"type":"text",
"title":"回車複製到粘貼板",
"subtitle":"回車複製到粘貼板",
"arg":"|────README.md |────go.mod |────go.sum |────tree.go |────|image |────────example.gif |────|response |────────icon.go |────────info.go |────────response.go |────|workflow |────────打印目錄樹.alfredworkflow ",
"autocomplete":"false",
"icon":{
"type":"fileicon",
"path":"~/Desktop"
}
}
]
}
複製代碼
items表示的是一個列表數組,列表有以下屬性
所以想要得到items的相關信息,得對前面的go腳本進行修改,下面是修改後的結果
tree.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"print/response"
"sort"
"strings"
)
const (
DIR = iota
FILE
)
func main() {
args := os.Args
if args == nil || len(args) < 2 {
Usage()
return
}
currentPath := args[1]
files, _ := ioutil.ReadDir(currentPath)
lastDirPos := sortFile(&files)
deep := 1
tree := ""
traverse(files, lastDirPos, deep, currentPath, &tree)
info := response.Info{}
icon := response.Icon{IType: "fileicon", Path: "~/Desktop"}
info.SetProperties(response.GetUID(), "text", "回車複製到粘貼板", "回車複製到粘貼板", tree, "false", icon)
var result response.Response
var infos []response.Info
infos = append(infos, info)
result.Items = infos
jsonStr, _ := json.Marshal(result)
fmt.Println(string(jsonStr))
}
func traverse(infos []os.FileInfo, lastDirPos, deep int, currentPath string, tree *string) {
prefix := "|"
length := len(infos)
for j := lastDirPos + 1; j < length; j++ {
if strings.Index(infos[j].Name(), ".") == 0 {
continue
}
*tree += printName(infos[j], prefix, deep, FILE)
}
for i := 0; i <= lastDirPos; i++ {
if strings.Index(infos[i].Name(), ".") == 0 {
continue
}
dirPath := currentPath + "/" + infos[i].Name()
files, _ := ioutil.ReadDir(dirPath)
if len(files) == 0 {
*tree += printName(infos[i], prefix, deep, DIR)
continue
}
lastDirPosC := sortFile(&files)
*tree += printName(infos[i], prefix, deep, DIR)
traverse(files, lastDirPosC, deep+1, dirPath, tree)
}
}
func printName(file os.FileInfo, prefix string, deep int, fileType int) string {
var placeHolder string
switch fileType {
case DIR:
placeHolder = strings.Repeat("────", deep) + "|"
break
case FILE:
placeHolder = strings.Repeat("────", deep)
}
return fmt.Sprintln(prefix + placeHolder + file.Name())
}
func sortFile(infos *[]os.FileInfo) int {
lastDirPos := len(*infos) - 1
adjustPos(*infos, &lastDirPos)
for i := 0; i < lastDirPos; i++ {
if !(*infos)[i].IsDir() {
swap(*infos, i, lastDirPos)
adjustPos(*infos, &lastDirPos)
}
}
dirSlice := (*infos)[:lastDirPos+1]
fileSlice := (*infos)[lastDirPos+1:]
sort.Slice(dirSlice, func(i, j int) bool { return dirSlice[i].Name() < dirSlice[j].Name() })
sort.Slice(fileSlice, func(i, j int) bool { return fileSlice[i].Name() < fileSlice[j].Name() })
merge := append(dirSlice, fileSlice...)
infos = &merge
return lastDirPos
}
func swap(infos []os.FileInfo, i, j int) {
temp := infos[i]
infos[i] = infos[j]
infos[j] = temp
}
func adjustPos(infos []os.FileInfo, lastDirPos *int) {
for !infos[*lastDirPos].IsDir() {
*lastDirPos--
if *lastDirPos == -1 {
break
}
}
}
var Usage = func() {
fmt.Println("input param")
}
=====================================
icon.go
package response
type Icon struct {
IType string `json:"type"`
Path string `json:"path"`
}
====================================
info.go
package response
import (
"crypto/rand"
"fmt"
)
type Info struct {
Uid string `json:"uid"`
IType string `json:"type"`
Title string `json:"title"`
Subtitle string `json:"subtitle"`
Arg string `json:"arg"`
Autocomplete string `json:"autocomplete"`
Icon Icon `json:"icon"`
}
func(i *Info) SetProperties(uid,itype,title,subtitle,arg,autocomplete string,icon Icon){
i.Uid=uid
i.IType=itype
i.Title=title
i.Subtitle=subtitle
i.Arg=arg
i.Autocomplete=autocomplete
i.Icon=icon
}
func GetUID() string {
data := make([]byte, 16)
_, err := rand.Read(data)
if err != nil {
panic(err)
}
uuid := fmt.Sprintf("%X-%X-%X-%X-%X", data[0:4], data[4:6], data[6:8], data[8:10], data[10:])
return uuid
}
====================================
response.go
package response
type Response struct {
Items []Info `json:"items"`
}
====================================
代碼目錄結構:
|────README.md
|────go.mod
|────go.sum
|────tree.go
|────|response
|────────icon.go
|────────info.go
|────────response.go
複製代碼
經過go build 或者go install 得到名稱爲tree的可執行文件 而後按照以下步驟:
|────README.md
|────go.mod
|────go.sum
|────tree.go
|────|image
|────────example.gif
|────|response
|────────icon.go
|────────info.go
|────────response.go
|────|workflow
|────────打印目錄樹.alfredworkflow
複製代碼
對這裏就學會了go和workflow結合打印目錄樹,附上github地址,給爲給個贊吧👍