golang - ls Posted on 2017-11-07 | In Golang Golang实现ls命令基本功能v1 同步版本package mainimport ( "fmt" "io/ioutil" "log" "math" "os" "strings" "unicode/utf8" "github.com/fatih/color" "golang.org/x/crypto/ssh/terminal")func main() { // 读取当前目录所有文件/文件夹 files, err := ioutil.ReadDir(".") if err != nil { log.Fatal(err) } // 读取终端的宽度,后续许计算什么时候折行 width, _, err := terminal.GetSize(int(os.Stdin.Fd())) if err != nil { log.Fatal(err) } // 保持每一项最小间距一个空格 max := getMaxLen(files) + 1 // 每一行最多放多少个输出项 countsEachRow := math.Floor(float64(width) / max) // 输出显示 printText(unHiddenFiles(files), int(max), int(countsEachRow))}// 获取所有文件总文件名字最长的长度,为了能够输出时对其func getMaxLen(files []os.FileInfo) (length float64) { length = 0 for _, file := range files { // 计算的时候排除掉隐藏文件 if !strings.HasPrefix(file.Name(), ".") { length = math.Max(float64(utf8.RuneCountInString(file.Name())), length) } } return}func printText(files []os.FileInfo, max int, num int) { // 对文件夹和可执行命令输出不同颜色 FgCyan := color.New(color.FgCyan).PrintfFunc() red := color.New(color.FgRed).PrintfFunc() for index, file := range files { // 获取文件本身的长度 length := utf8.RuneCountInString(file.Name()) // 不足长度的用空格补齐 fileName := file.Name() + strings.Repeat(" ", max-length) // 对文件夹处理 if file.IsDir() { FgCyan(fileName) // 每行的最后一项需要主动换行 if index%num == 0 { fmt.Println() } continue } // 对可执行文件处理 if strings.Count(file.Mode().String(), "x") == 3 { red(fileName) if index%num == 0 { fmt.Println() } continue } // 对普通文件处理 fmt.Printf(fileName) if index%num == 0 { fmt.Println() } }}// 过滤掉隐藏文件func unHiddenFiles(files []os.FileInfo) (arr []os.FileInfo) { arr = make([]os.FileInfo, 0) for _, file := range files { if !strings.HasPrefix(file.Name(), ".") { arr = append(arr, file) } } return} v2 并发执行package mainimport ( "fmt" "io/ioutil" "log" "math" "os" "strings" "sync" "unicode/utf8" "github.com/fatih/color" "golang.org/x/crypto/ssh/terminal")// ch中的文件结构type fileModel struct { name string category int}func main() { // 运送需要输出的内容的ch names := make(chan fileModel) files, err := ioutil.ReadDir(".") if err != nil { log.Fatal(err) } width, _, err := terminal.GetSize(int(os.Stdin.Fd())) if err != nil { log.Fatal(err) } max := getMaxLen(files) + 1 countsEachRow := math.Floor(float64(width) / max) printText(files, int(max), names) FgCyan := color.New(color.FgCyan).PrintfFunc() red := color.New(color.FgRed).PrintfFunc() // 因为是已知的长度,可以根据是否是每行最后一项和类型做不同处理 for index := 0; index < len(files); index++ { name := <-names switch category := name.category; category { case 0: // 文件夹处理 FgCyan(name.name) case 1: // 可执行文件处理 red(name.name) default: // 普通文件处理 fmt.Print(name.name) } // 是否主动换行判断 if index%int(countsEachRow) == 0 { fmt.Println() } }}func getMaxLen(files []os.FileInfo) (length float64) { length = 0 for _, file := range files { if !strings.HasPrefix(file.Name(), ".") { length = math.Max(float64(utf8.RuneCountInString(file.Name())), length) } } return}func printText(files []os.FileInfo, max int, names chan<- fileModel) { // 用wg让所有goroutines结束后再让main goroutine结束 var wg sync.WaitGroup for _, file := range files { wg.Add(1) go func(file os.FileInfo) { defer wg.Done() if !strings.HasPrefix(file.Name(), ".") { length := utf8.RuneCountInString(file.Name()) fileName := file.Name() + strings.Repeat(" ", max-length) if file.IsDir() { // 传入到ch names <- fileModel{fileName, 0} return } if strings.Count(file.Mode().String(), "x") == 3 { // 传入到ch names <- fileModel{fileName, 1} return } // 传入到ch names <- fileModel{fileName, 2} } }(file) } go func() { wg.Wait() // 所有goroutines结束后结束ch close(names) }()}