> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/08.0/08.16.md).

# 08.16 遍历目录树

本节将介绍`Go`编写的`find(1)`命令行程序的一个相对简单的版本。

程序名为`gofind.go`，分为四部分介绍。`gofind.go`的代码将支持两个命令行选项。`-d`选项在作为目录的路径前面打印一个星形字符，而`-f`选项在作为常规文件的路径前面打印一个加号字符。

`goFind.go`第一部分代码如下：

```go
package main

import (
    "flag"
    "fmt"
    "os"
    "path/filepath"
)
```

如你所见，`goFind.go`程序使用`flag`包高效地处理命令行参数。

`goFind.go`第二部分代码如下：

```go
var minusD bool = false
var minusF bool = false

func walk(path string, info os.FileInfo, err error) error {
    fileInfo, err := os.Stat(path)
    if err != nil {
        return err
    }
    mode := fileInfo.Mode()
    if mode.IsRegular() && minusF {
        fmt.Println("+", path)
        return nil
    }
    if mode.IsDir() && minusD {
        fmt.Println("*", path)
        return nil
    }
    fmt.Println(path)
    return nil
}
```

由于`minusD`和`minusF`应该可以从程序中的任何地方访问，包括`walk（）`函数，我决定将它们设置为全局变量。`IsDir（)`函数用于识别目录，`isRegular（）`用于识别常规文件。

`goFind.go`第三部分代码如下：

```go
func main() {
    starD := flag.Bool("d", false, "Signify directories")
    plusF := flag.Bool("f", false, "Signify regular files")
    flag.Parse()
    flags := flag.Args()
    Path := "."
    if len(flags) == 1 {
        Path = flags[0]
    }
```

`goFind.go`最后一部分代码如下：

```go
    minusD = *starD
    minusF = *plusF
    err := filepath.Walk(Path, walk)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}
```

执行`goFind.go`将创建如下的输出：

```
$ go run goFind.go -d -f /tmp/
* /tmp/
+ /tmp/.keystone_install_lock
* /tmp/5580C65A-E7E2-4B27-AD91-506F85545E1D
* /tmp/569A57CB-8FD3-4879-A6A3-B86116CB0116
+ /tmp/ExmanProcessMutex
+ /tmp/adobegc.log
/tmp/com.adobe.AdobeIPCBroker.ctrl-mtsouk
* /tmp/com.apple.launchd.h3Izgq45dz
/tmp/com.apple.launchd.h3Izgq45dz/Listeners
* /tmp/lilo.46843
+ /tmp/swtag.log
/tmp/textmate-501.sock
$ go run goFind.go -f /tmp/
/tmp/
+ /tmp/.keystone_install_lock
/tmp/5580C65A-E7E2-4B27-AD91-506F85545E1D
/tmp/569A57CB-8FD3-4879-A6A3-B86116CB0116
+ /tmp/ExmanProcessMutex
+ /tmp/adobegc.log
/tmp/com.adobe.AdobeIPCBroker.ctrl-mtsouk
/tmp/com.apple.launchd.h3Izgq45dz
/tmp/com.apple.launchd.h3Izgq45dz/Listeners
/tmp/lilo.46843
+ /tmp/swtag.log
/tmp/textmate-501.sock
$ go run goFind.go /tmp/
/tmp/
/tmp/.keystone_install_lock
/tmp/5580C65A-E7E2-4B27-AD91-506F85545E1D
/tmp/569A57CB-8FD3-4879-A6A3-B86116CB0116
/tmp/ExmanProcessMutex
/tmp/adobegc.log
/tmp/com.adobe.AdobeIPCBroker.ctrl-mtsouk
/tmp/com.apple.launchd.h3Izgq45dz
/tmp/com.apple.launchd.h3Izgq45dz/Listeners
/tmp/lilo.46843
/tmp/swtag.log
/tmp/textmate-501.sock
```

`goFind.go`的输出包含了指定根目录下的所有文件类型，包括套接字和符号链接。如果对walk（）函数进行必要的更改，你可以告诉`gofind.go`只打印你真正感兴趣的信息。这是留给你做的练习。

> *现在想象一下使用`C`编程语言的标准库函数来实现相同的程序有多困难！*
