目录
- 1.介绍Overview
- 2.获取当前操作系统的目录分割符Separator
- 3.获取绝对路径Abs
- 4.获取路径中的Base
- 5.路径清理Clean
- 6.将字符串中的目录路径输出Dir
- 7.评估符号链接EvalSymlinks
- 8.获取文件扩展名Ext
- 9.处理/替换路径分隔符FromSlash
- 10.模式匹配-查找/统计文件数量Glob
- 11.(Go1.7版本或更高版本不建议)HasPrefix
- 12.报告路径是否为绝对路径IsAbs
- 13.路径链接的正确姿势Join
- 14.模式匹配Match
- 15.获取相对路径Rel
- 16.路径中的目录与文件名分割Split
- 17.路径元素分割SplitList
- 18.统一转换分割符为Linux风格(左斜杠)ToSlash
- 19.卷名称获取VolumeName
- 20.遍历磁盘Walk与WalkFunc
介绍
filepath是跨平台的(Windows/Linux/Unix/Plan9)
考虑到官网的pkg文档实在是对初学者不友好,所以
我专门针对案例使用了Windows系统下的操作案例。
初学者在看到官网的example时,往往不是使用Linux
系统,所以案例会让初学者感到陌生。才专门开这样
的文章。
获取当前操作系统的目录分割符
通过常量获取:
sep := string(filepath.Separator)
例如我们在windows中,输出的就是“\”符号,在Linux系统中就是“/”符号,使用这个常量的目的是,我们编写跨平台程序的时候,如果需要操作目录/文件,往往我们需要链接目录或文件的字符串,如果我们直接输入“/”或则“\”那么不利于跨平台,所以当我们需要链接文件名称/目录名称的时候,需要使用分割符时,请使用 filepath.Separator
常量。记得string转换一下类型,因为其本身的类型是 untype rune
获取绝对路径
函数签名:
func Abs(path string) (string, error)
例如我想知道我编写的go程序编译后,运行时所处于的磁盘的路径(绝对路径)可以这样:
abspath, err := filepath.Abs(".")
if err != nil {
panic(err)
}
abspath
你以可以理解为 current path
,即程序执行时所处的目录路径。
当然,这是一个附带功能,毕竟 Abs()
主要的意思还是“返回路径的绝对表示”,只是当给定的路径不是绝对路径时,才将其与当前工作路径链接,形成一个绝对路径。
获取路径Base
假如我当前程序所在的路径为:“D:\go_project\src\test”
即我的程序所处的目录是N级,上面这个,我程序所处的目录级别分别是:D:、go_project、src、test,就N为4级。那么我要获取程序所在的目录名称,而不是完整的目录名称,我应该这样做:
abspath, err := filepath.Abs(".")
if err != nil {
panic(err)
}
base := filepath.Base(abspath)
Base()
函数将会把输入的路径字符串内容,取出最后部分,当然如果最后一个字符为分隔符,它会先删除分隔符。
如果路径为空,则会返回".“及当前目录的意思,这样一个字符串内容。如果全部是分割符组成的话,那么Base函数会返回一个”“或”/"的分割符字符串。这里的 Base()
,可以理解为目录路径所指向的最后目的地(Base Location)
Clean暂无例子,仅翻译:
Clean()
通过纯粹的词法处理返回等同于path的最短路径名。 它迭代应用以下规则,直到无法进行进一步处理:
1.用一个替换多个Separator元素。
2.消除每一个。 路径名元素(当前目录)。
3.消除每个内部..路径名称元素(父目录)
以及它之前的非..元素。
4.消除开始根路径的元素:
也就是说,在路径的开头用“/”替换“/ ..”,
假设Separator是'/'。
只有当它表示根目录时,返回的路径以斜杠结尾,例如Unix上的 “/” 或Windows上的 “X:\” 。
最后,任何出现的斜杠都会被 Separator
替换。
如果此过程的结果是空字符串,则 Clean()
返回字符串 “.” 。
这个例子不用写,可以理解为帮我们清理路径字符串中的斜杠,以当前操作系统的
filepath.Separator
作为标准替换。
另请参阅Rob Pike的“Plan 9中的词汇文件名称或获得Dot-Dot Right”https://9p.io/sys/doc/lexnames.html
识别路径字符串中的目录路径
假如在Linux系统中或Windows系统中,程序处理如下列这样一些路径字符串:
“D:/go_project/src/test/go.mod”
“D:/go_project/src/test/readme”
“~/.vim”
“~/.vimrc”
这里就有问题了!例如go.mod是一个文件还是一个目录?我们会go语言的同学一看就知道这绝逼应该是一个文件,那么readme是文件还是一个目录呢?这个就不好说了。毕竟没有.ext 扩展名,文件还是目录,就是我们人类都要 ls -l 或 dir 命令看看吧?包括下面的.vim是目录还是文件,.vimrc是文件还是目录?对于程序来讲,必须要明确,所以我们可以通过 filepath.Dir()
函数来处理这个事情。
path := "D:/go_project/src/test/go.mod"
fmt.Println(filepath.Dir(path))
运行输出的结果:
PS D:\go_project\src\test> go test --test.run TestFilePath
D:\go_project\src\test
PASS
ok test 0.117s
D:\go_project\src\test
我们可以看到,程序中,我故意使用了/而不是\来作为路径分隔符,但是 Dir()
函数执行后的结果来看,已经帮我们转换成了当前操作系统的分隔符了。如果我们给定的字符串参数是空字符串"",则输出 “.” 表明当前目录,如果字符串全部是分隔符,则会输出最后一个分割符号作为结果。
EvalSymlinks 暂无例子
在评估任何符号链接后,EvalSymlinks()
返回路径名。 如果path是相对的,则结果将相对于当前目录,除非其中一个组件是绝对符号链接。 EvalSymlinks()
在结果上调用Clean。
func EvalSymlinks(path string) (string, error)
Eval 是Evaluation评估的意思,Sym 是Symbol符号的意思,合起来就是评估/评测符号链接的意思。
假
如
我
传
入
的
参
数
字
符
串
,
是
一
个
符
号
链
接
的
相
对
/
绝
对
路
径
\color{#ff0000}{假如我传入的参数字符串,是一个符号链接的相对/绝对路径}
假如我传入的参数字符串,是一个符号链接的相对/绝对路径
返回的结果内容会再调用一次Clean进行输出。如果符号链接文件的路径是相对的,返回相对,是绝对的返回绝对,如果符号链接指向的链接目标的路径是绝对的,则返回其绝对路径,就这么个意思。
获取文件扩展名Ext
当我们确定了一个文件的路径后,我们想要将这个文件的扩展名提取出来,可以这样:
path := "D:/go_project/src/test/go.mod"
extname := filepath.Ext(path)
fmt.Println(extname)
输出结果:
PS D:\go_project\src\test> go test --test.run TestFilePath
.mod
PASS
ok test 0.328s
.mod
结果就是获取到这个文件的扩展是.mod,如果不要这个点可以:
if len(extname) > 1 {
if strings.Contains(extname, ".") {
extname = extname[1:]
}
}
首先扩展名如果能够获取到,那么一定是>1的长度哪怕是一个 .o 或 .a 这样的文件扩展,那么也可以不用 strings.Contains
判断一下,而直接使用 extname = extname[1:]
这样就可以将那个.号给去除了。不要怀疑,如果文件名是一个 xxxx.
这样的形式,文件名中的.会被作为返回值,但长度为1,而不是 > 1。
如果文件名是 xxxx
,没有.没有.后面的扩展名,则返回值为空串 “”,如果文件名是 github.com.readme
那么扩展名返回的是最后一个点后的内容,即: .readme
。
处理/替换路径分隔符
path := "D:/go_project/src/test/go.mod"
fromSlash := filepath.FromSlash(path)
fmt.Println(fromSlash)
输出结果:
PS D:\go_project\src\test> go test --test.run TestFilePath
D:\go_project\src\test\go.mod
PASS
ok test 0.379s
FromSlash
的作用就是将路径中的分割符统一为当前操作系统中的分割符号。内部是其实用的是 strings.ReplaceAll
函数处理:
所以这个函数,与Clean不同的是,它仅仅是将路径中的分割符做一次标准化替换而已。
我们也可以看到源代码中的第一次判断,即分隔符如果是Linux操作系统“/”,这直接返回path,也就是不处理。换句话说,Windows系统中处理含有 /
的路径字符串才会有效果。
模式匹配-查找/统计文件数量
函数签名:
func Glob(pattern string) (matches []string, err error)
这里先给出代码例子:
var (
matches []string
err error
)
pattern := "C:\\Windows\\System32\\*.exe"
if matches, err = filepath.Glob(pattern); err != nil {
panic(err)
}
fmt.Println(len(matches))
作用是,通过将通配符*加入路径,形成模式pattern字符串,然后作为参数传递给Glob,目的是统计C:\Windows\System32目录中到底有多少个exe文件。当然matches字符串数组内存放的是没有给.exe文件的绝对路径(包含文件名),太长就不贴这里了。
上面的代码输出:
PS D:\go_project\src\test> go test --test.run TestFilePath
613
PASS
ok test 0.189s
也就是找到了613个exe文件。
如果没有匹配的文件,Glob将返回匹配pattern或nil的所有文件的名称。 模式的语法与Match中的相同。 该模式可以描述分层名称,例如/ usr / * / bin / ed(假设分隔符为’/’)。
G
l
o
b
忽
略
文
件
系
统
错
误
,
例
如
读
取
目
录
的
I
/
O
错
误
。
\color{#ff0000}{Glob忽略文件系统错误,例如读取目录的I / O错误。 }
Glob忽略文件系统错误,例如读取目录的I/O错误。
当
模
式
格
式
错
误
时
,
唯
一
可
能
返
回
的
错
误
是
E
r
r
B
a
d
P
a
t
t
e
r
n
。
\color{#ff0000}{当模式格式错误时,唯一可能返回的错误是ErrBadPattern。}
当模式格式错误时,唯一可能返回的错误是ErrBadPattern。
不推荐使用HasPrefix
HasPrefix存在历史兼容性,不应使用。
不推荐使用:HasPrefix不遵循路径边界,并且在需要时不会忽略大小写。
报告路径是否为绝对路径IsAbs
func IsAbs(path string) (b bool)
IsAbs reports whether the path is absolute. // 这个和Abs的意思差不多,不赘述了。
路径链接的正确姿势Join
我们链接路径字符串这样吗?
programPath := APath + string(filepath.Separator) + BPath
… 这样做没问题,但缺乏规范,而且如果APath的内容是:/home/gopher/.vim/bundle/,那么我们再链接就变成//了,这怕是不太好。
所以规范的路径元素添加(链接字符串)操作应该使用filepath包给我们提供的Join函数。
下面的一个例子是,我从系统环境变量中得到Windows系统的安装路径,因为这个可能不是标准的C:\Windows,也许是D:\Win10也是说不定的事情,所以通过系统的环境变量来获取是最安全的。然后在链接目录路径元素System32,例子如下:
env := os.Environ()
var envMap = make(map[string]string)
for _, v := range env {
kv := strings.Split(v, "=")
envMap[kv[0]] = kv[1]
}
v, ok := envMap["windir"]
if ok {
sys32 := filepath.Join(v, "System32") // 将System32路径元素添加到 v 这个路径元素后面
fmt.Println(sys32)
}
输出结果:
PS D:\go_project\src\test> go test --test.run TestFilePath
C:\WINDOWS\System32
PASS
ok test 0.654s
这样做的好处是,我们不用关心系统的Separator,Join函数的签名:
func Join(elem ...string) string
形参列表是多参数式,可以链接多个路径元素。
Join将任意数量的路径元素连接到单个路径中,必要时添加Separator。 在结果上调用Clean; 特别是,忽略所有空字符串。 在Windows上,当且仅当第一个路径元素是UNC路径时,结果才是UNC路径。
(UNC Universal Naming Convention 通用命名惯例)
例如:
file://127.0.0.1/C:/ 本地C盘
file://localhost/D:/ 本地D盘
file:///F:/ 本地F盘
模式匹配Match
函数签名:
func Match(pattern, name string) (matched bool, err error)
匹配报告名称是否与shell文件名模式匹配。 模式语法是:
pattern模式:
{term}
term术语:
'*' 匹配任何非分隔符字符序列
'?' 匹配任何单个非分隔符
'[' ['^'] { character-range } ']'
字符类 (必须是非空的)
c 匹配字符 c (c != '*','?','\\','[')
'\\'c 匹配字符 c
character-range字符范围:
c 匹配字符 c (c!='\\','-',']')
'\\' c 匹配字符 c
lo '-' hi 匹配字符c表示 lo <= c <= hi
匹配需要模式匹配所有名称,而不仅仅是子字符串。 当模式格式错误时,唯一可能返回的错误是 ErrBadPattern
。
在Windows上,禁用转义。 相反,’\'被视为路径分隔符。
比如看看下面这个例子,我们匹配error.log的模式:
var (
err error
ret bool
)
fileName := "error.log"
pattern1 := "*ros*"
pattern2 := "*ror*"
if ret, err = filepath.Match(pattern1, fileName); err != nil {
panic(err)
}
fmt.Println(ret)
if ret, err = filepath.Match(pattern2, fileName); err != nil {
panic(err)
}
fmt.Println(ret)
输出结果:
PS D:\go_project\src\test> go test --test.run TestFilePath
false
true
PASS
ok test 0.193s
第一个模式匹配 ros 对 error.log 显然是不匹配,而第二个模式 ror 匹配到了 error.log ,换句话说,我的模式和路径/文件名是否是模式匹配的。当然,模式匹配的是字符串,字符串内容,即文件名以及文件/路径是否存在,不是这个函数考虑的事情。
再来一个例子:
var (
err error
ret bool
)
fileName := "data20190411.log"
pattern := "data[{0-9}]*"
if ret, err = filepath.Match(pattern, fileName); err != nil {
panic(err)
}
fmt.Println(ret)
再来一个对比一下:
var (
err error
ret bool
)
fileName := "data20190411.log"
pattern := "data201905[0-9]*"
if ret, err = filepath.Match(pattern, fileName); err != nil {
panic(err)
}
fmt.Println(ret)
上上面的例子,我的模式是"data[{0-9}]*" 那么可以匹配到 datayyyymmdd.log
,没啥可说的。
上面的例子,我们需要匹配5月份的所有.log文件,那么文件名是04月份的,肯定就匹配不到。
这个Match函数还是很重要,我们从目录拿到了所有文件名后,我们可以直接通过filepath.Match()函数去模式匹配我们需要的文件。
当然这个事情使用regexp包(正则表达式包)也能解决,但是我们需要:
pattern := "xxxxxxx"
reg := regexp.MusthCompile(pattern)
reg.MatchString(fileName)
但原则是我们如果能少导包就少 import pacakge
,路径/文件的处理能用 filepath
完成的话就尽可能只用它,除非我们要做的功能它无法覆盖到,才去导入对应的功能包。
获取相对路径Rel
函数签名:
func Rel(basepath, targpath string) (string, error)
Rel返回一个相对路径,该路径在使用插入分隔符连接到basepath时在词法上等效于targpath。 也就是说,Join(basepath,Rel(basepath,targpath))等同于targpath本身。 成功时,返回的路径将始终相对于basepath,即使basepath和targpath不共享任何元素。 如果无法相对于basepath创建targpath,或者如果需要知道当前工作目录,则会返回错误。 Rel调用结果清理。
那么我们来看看在Windows系统下的一个小例子:
path := []string{
"D:\\go_project\\src\\test",
"D:\\go_project\\src",
"D:\\go_project",
}
base := "D:\\go_project\\src"
var ret string
for _, v := range path {
ret, _ = filepath.Rel(base, v)
fmt.Println(ret)
}
我们看看输出结果:
PS D:\go_project\src\test> go test --test.run TestFilePath
test
.
..
PASS
ok test 0.379s
首先我给定的base,即 Rel()
的第一个参数是:"D:/go_project/src"
, 而第二个处理的参数分别来自与 path []string
, 分别是:
D:/go_project/src/test
D:/go_project/src
D:/go_project
=> 将以上三个路径分别和 base => D:/go_project/src 进行相对路径求解
我们看到执行的结果分别是:
test
.
..
即:
D:/go_project/src -> D:/go_project/src/test 求出的相对路径是 test
D:/go_project/src -> D:/go_project/src 求出的相对路径是 . (即当前目录)
D:/go_project -> D:/go_project/src 求出的相对路径是 .. (即上一级目录)
换句话说,Rel(base, target string)
第一个参数,是我们需要处理的路径,相对于第二个目标路径进行处理,获取两者之间的关系,是:
./test
.
…
路径中的目录与文件名分割Split
函数签名:
func Split(path string) (dir, file string)
Split()
将最后的Separator之后的路径立即分割,将其分成目录和文件名组件。 如果路径中没有Separator
,则Split()
会返回一个空目录,并将文件设置为path。 返回的值具有path = dir + file的属性。
我们看看下面的例子:
path := "D:\\go_project\\src\\test\\abc"
dir, file := filepath.Split(path)
fmt.Println(dir)
fmt.Println(file)
执行结果:
PS D:\go_project\src\test> go test --test.run TestFilePath
D:\go_project\src\test\
abc
PASS
ok test 0.468s
Split()
分
割
,
并
不
会
检
测
最
后
一
个
分
割
符
后
的
是
目
录
还
是
文
件
\color{#ff0000}{分割,并不会检测最后一个分割符后的是目录还是文件}
分割,并不会检测最后一个分割符后的是目录还是文件
从功能上来说,Split()
只是将路径中,最后一个分割符后的内容分割出来返回file
,将分割后前面的内容返回到dir
,但实际上我例子中实际情况是abc
是目录,而不是文件,所以安全的做法是,在分割之前要想办法判断出,路径是指向的是目录还是文件,确定是文件后,才进行Split()
分割。
路径元素分割SplitList
函数签名:
func SplitList(path string) []string
SplitList()
拆分由特定于操作系统的 ListSeparator
连接的路径列表,通常位于PATH或GOPATH环境变量中。 与 strings.Split()
不同,SplitList()
在传递空字符串时返回空切片。
一个小例子:
env := os.Environ()
var envMap = make(map[string]string)
for _, v := range env {
kv := strings.Split(v, "=")
envMap[kv[0]] = kv[1]
}
v, ok := envMap["Path"]
if ok {
pathItem := filepath.SplitList(v)
for _, v := range pathItem {
fmt.Println(v)
}
}
我们先从 os.Environ()
获取环境变量 []string
,string数组中的存放形式是: k=v,例如"windir=C:\Windows",所以我们使用 strings.Split()
将其分割并写入 envMap map[string]string
中,那么我们需要将系统的环境变量 Path
的内容使用 filepath.SplitList
分割出来。
输出结果如下:
PS D:\go_project\src\test> go test --test.run TestFilePath
C:\WINDOWS
C:\WINDOWS\System32\Wbem
C:\WINDOWS\System32\WindowsPowerShell\v1.0\
C:\Users\gamec\.dnx\bin
C:\Program Files\Microsoft DNX\Dnvm\
C:\Program Files\Microsoft SQL Server\120\Tools\Binn\
C:\Program Files\Microsoft SQL Server\130\Tools\Binn\
C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\
C:\Program Files (x86)\Microsoft Emulator Manager\1.0\
C:\Program Files (x86)\GTK2-Runtime\bin
D:\Applications\phpStudy32\php56n
D:\phalcon-devtools-master
C:\Program Files\nodejs\
C:\Program Files\Java\jdk1.8.0_91\bin
D:\apache-ant-1.9.7\bin
C:\ProgramData\ComposerSetup\bin
D:\ffmpeg\bin
D:\Applications\sqlite3
D:\Android\Android Studio\gradle\gradle-5.4.1\bin
D:\Applications\phpStudy_2016_x64\php56n
C:\WINDOWS\system32
C:\Program Files\TortoiseGit\bin
C:\Program Files (x86)\GtkSharp\2.12\bin
C:\WINDOWS\System32\OpenSSH\
D:\Applications\ffmpeg-4.1.1-win64-static\bin
%GOPATH%\bin
D:\Applications\Microsoft VS Code\bin
%REDISPATH%
D:\Applications\Git\cmd
D:\Go\bin
%GOPATH%\bin
%GOPATH%
C:\Program Files (x86)\Bitvise SSH Client
%UPXHOME%
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin
C:\Users\gamec\AppData\Local\VanDyke Software\Clients\
D:\Applications\Microsoft VS Code\bin
C:\Users\gamec\AppData\Roaming\npm
C:\Users\gamec\AppData\Roaming\Composer\vendor\bin
C:\Users\gamec\AppData\Local\Microsoft\WindowsApps
C:\Users\gamec\go\bin
PASS
ok test 0.814s
这时候的 pathItem []string
已经获取了系统的 Path
的所有分割项了。
将Win风格的路径分割符转换为Linux的分隔符ToSlash
函数签名:
func ToSlash(path string) string
ToSlash()
返回用斜杠(’/’)字符替换路径中每个分隔符的结果。 多个分隔符由多个斜杠替换。
那这次的例子我还是利用上面的例子修改一下:
env := os.Environ()
var envMap = make(map[string]string)
for _, v := range env {
kv := strings.Split(v, "=")
envMap[kv[0]] = kv[1]
}
v, ok := envMap["Path"]
if ok {
pathItem := filepath.SplitList(v)
for _, v := range pathItem {
pslash := filepath.ToSlash(v) // 这里我用ToSlash处理后,\ 分割符全部处理成 / 分割符
fmt.Println(pslash)
}
}
输出结果这里不贴了。因为就是将有斜杠分隔符全部处理成左斜杠分隔符。
卷名称获取VolumeName
函数签名:
func VolumeName(path string) string
VolumeName()
返回前导卷名称。 给定“C:\foo\bar”,它在Windows上返回“C:”。 给定“\host\share\foo”,它返回“\host\share”。 在其他平台上它返回""(空字符串)。(支持Windows/UNC)
例子A:
path := "C:\\Windows\\System32\\"
volumeName := filepath.VolumeName(path)
fmt.Println(volumeName)
得到的结果:
C:
表明卷名称为"C:", AKA"分区的盘符号"(windows A-Z, A,B是软盘 C~Z可能是硬盘分区的盘符,可能是Z倒叙过来的几个常常用于网路映射的盘符)
例子B:
path := "\\\\192.168.0.108\\share"
volumeName := filepath.VolumeName(path)
fmt.Println(volumeName)
执行的结果是:
PS D:\go_project\src\test> go test --test.run TestFilePath
\\192.168.0.108\share
PASS
ok test 0.125s
\\192.168.0.108
是局域网中,某台Windows的系统,开启了网络共享后,我们可以通过 \\host\xxxx
来访问的路径。四个右斜杠是用来转义 \\
的。
遍历磁盘Walk与WalkFunc
函数签名:
func Walk(root string, walkFn WalkFunc) error
Walk遍历以root为根的文件树,为树中的每个文件或目录调用walkFn,包括root。 访问文件和目录时出现的所有错误都由walkFn过滤。 这些文件以词法顺序进行,这使得输出具有确定性,但这意味着对于非常大的目录,Walk可能效率低下。 步行不遵循符号链接。
例子:
/*
_____ __ __ _ __
╱ ____| | ╲/ | | |/ /
| | __ ___ | ╲ / | __ _ _ __| ' /
| | |_ |/ _ ╲| |╲ /| |/ _` | '__| <
| |__| | __/| | | ( _| | | | . ╲
╲_____|╲___ |_| |_|╲__,_ |_| |_|╲_╲
可爱飞行猪❤: golang83@outlook.com ???
Author Name: GeMarK.VK.Chow奥迪哥 ???
Creaet Time: 2019/05/18 - 19:25:03
ProgramFile: test.go
Description:
遍历磁盘Walk 与 WalkFunc
*/
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"time"
)
var (
err error
slog *log.Logger
ptrFS *os.File
logFlags int
fileFlags int
count uint32
)
func init() {
logFlags = log.Ldate | log.Lmicroseconds | log.Lshortfile
fileFlags = os.O_CREATE | os.O_RDWR | os.O_APPEND
ptrFS, err = os.OpenFile("error.log", fileFlags, 0)
if err != nil {
fmt.Println("OpenLogFileError:", err)
os.Exit(1)
}
slog = new(log.Logger)
slog.SetPrefix("[TestInfo]")
slog.SetFlags(logFlags)
slog.SetOutput(ptrFS)
}
func main() {
// Unified processing panic 统一处理panic
defer errorHandle()
walk()
}
func walk() {
// Testing stdout message. 输出消息
slog.Printf("this function its test filepath package Walk function.\r\n")
// count time
start := time.Now()
// set root is C:\ 定义根目录为 C 盘根目录
root := "C:\\"
// 调用 filepath.Walk 函数(root, 函数对象参数(路径,文件信息,错误)返回error对象)
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
slog.Printf("%s\r\n", err)
// if return err, program will Exit(1)... so, skip err to continue program.
// 这里输出错误就好,不然程序就终止了....
}
if info.IsDir() {
count++
// fmt.Printf("Found Directory: [%s]\r\r\n", info.Name())
// 如果这里打印的话,太多多了,所以仅仅操作计数就可以了。
}
return nil
})
if err != nil {
panic(err)
}
end := time.Now().Sub(start)
// write info to log file 将统计信息写入日志文件
slog.Printf("Found: %d %s", count, " directories.\r\n")
slog.Printf("Consumes:%s", end.String())
fmt.Println("Walk func complete. Found:", count, " directories.", "Consumes:", end.String())
}
func errorHandle() {
if panic_rev := recover(); panic_rev != nil {
slog.Printf("ErrorsInfo:%s\r\n", panic_rev)
ptrFS.Close()
os.Exit(1)
}
}
当然,也有可能会出现某个目录看着存在,但实际上已经不存在了(例如Windows.old)我清理磁盘,使用清理系统文件,删除了Windows更新安装后,遗留的文件,这个时候,可能就会出现这样的目录,需要重新启动后,才能解决。当然系统中很有可能存在其他有问题的文件夹,上述的源代码会将这种意外错误记录在error.log中含有“ErrorsInfo”内容的行。而且也可以通过error.log文件中的信息了解哪些文件夹可能权限是只能Administrotar或者根本只能System权限才能访问的。如果没有致命错误,将会输出大致存在多少目录数,以及消费的时间。