在Go中列出目录

huangapple go评论100阅读模式
英文:

List directory in Go

问题

我一直在尝试找出如何在Go中简单地列出单个目录中的文件和文件夹。

我找到了filepath.Walk,但它会自动进入子目录,这不是我想要的。我所有的其他搜索都没有找到更好的解决方案。

我确定这个功能是存在的,但是很难找到。如果有人知道我应该去哪里找,请告诉我。谢谢。

英文:

I've been trying to figure out how to simply list the files and folders in a single directory in Go.

I've found filepath.Walk, but it goes into sub-directories automatically, which I don't want. All of my other searches haven't turned anything better up.

I'm sure that this functionality exists, but it's been really hard to find. Let me know if anyone knows where I should look. Thanks.

答案1

得分: 471

你可以尝试使用os包中的ReadDir函数。根据文档:

> ReadDir读取指定的目录,并按文件名对其所有目录条目进行排序。

返回的切片包含os.DirEntry类型,该类型提供了这里列出的方法。以下是一个基本示例,列出当前目录中所有内容的名称(包括文件夹,但没有特殊标记 - 你可以使用IsDir()方法来检查一个项目是否是文件夹):

package main

import (
    "fmt"
    "os"
    "log"
)

func main() {
    entries, err := os.ReadDir("./")
    if err != nil {
        log.Fatal(err)
    }
 
    for _, e := range entries {
        fmt.Println(e.Name())
    }
}
英文:

You can try using the ReadDir function in the os package. Per the docs:

> ReadDir reads the named directory, returning all its directory entries sorted by filename.

The resulting slice contains os.DirEntry types, which provide the methods listed here. Here is a basic example that lists the name of everything in the current directory (folders are included but not specially marked - you can check if an item is a folder by using the IsDir() method):

package main

import (
    "fmt"
    "os"
     "log"
)

func main() {
    entries, err := os.ReadDir("./")
    if err != nil {
        log.Fatal(err)
    }
 
    for _, e := range entries {
            fmt.Println(e.Name())
    }
}

答案2

得分: 112

我们可以使用各种golang标准库函数来获取文件系统中文件夹内的文件列表。

  1. filepath.Walk
  2. ioutil.ReadDir
  3. os.File.Readdir

<pre><code>
package main

import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
)

func main() {
var (
root string
files []string
err error
)

root := "/home/manigandan/golang/samples"
// filepath.Walk
files, err = FilePathWalkDir(root)
if err != nil {
	panic(err)
}
// ioutil.ReadDir
files, err = IOReadDir(root)
if err != nil {
	panic(err)
}
//os.File.Readdir
files, err = OSReadDir(root)
if err != nil {
	panic(err)
}

for _, file := range files {
	fmt.Println(file)
}

}
</code></pre>

  1. 使用filepath.Walk

> path/filepath包提供了一种方便的方式来扫描目录中的所有文件,它会自动扫描目录中的每个子目录。

func FilePathWalkDir(root string) ([]string, error) {
	var files []string
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			files = append(files, path)
		}
		return nil
	})
	return files, err
}
  1. 使用ioutil.ReadDir

> ioutil.ReadDir读取由dirname指定的目录,并按文件名排序返回一个目录条目列表。

func IOReadDir(root string) ([]string, error) {
	var files []string
	fileInfo, err := ioutil.ReadDir(root)
	if err != nil {
		return files, err
	}

	for _, file := range fileInfo {
		files = append(files, file.Name())
	}
	return files, nil
}
  1. 使用os.File.Readdir

> Readdir读取与文件关联的目录的内容,并按目录顺序返回最多n个FileInfo值的切片,就像Lstat返回的那样。对同一个文件的后续调用将产生更多的FileInfos。

func OSReadDir(root string) ([]string, error) {
	var files []string
	f, err := os.Open(root)
	if err != nil {
		return files, err
	}
	fileInfo, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return files, err
	}

	for _, file := range fileInfo {
		files = append(files, file.Name())
	}
	return files, nil
}

基准测试结果。

在Go中列出目录

获取更多详细信息,请参阅博客文章

英文:

We can get a list of files inside a folder on the file system using various golang standard library functions.

  1. filepath.Walk
  2. ioutil.ReadDir
  3. os.File.Readdir

<pre><code>
package main

import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
)

func main() {
var (
root string
files []string
err error
)

root := &quot;/home/manigandan/golang/samples&quot;
// filepath.Walk
files, err = FilePathWalkDir(root)
if err != nil {
	panic(err)
}
// ioutil.ReadDir
files, err = IOReadDir(root)
if err != nil {
	panic(err)
}
//os.File.Readdir
files, err = OSReadDir(root)
if err != nil {
	panic(err)
}

for _, file := range files {
	fmt.Println(file)
}

}
</code></pre>

  1. Using filepath.Walk

> The path/filepath package provides a handy way to scan all the files
> in a directory, it will automatically scan each sub-directories in the
> directory.

func FilePathWalkDir(root string) ([]string, error) {
	var files []string
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			files = append(files, path)
		}
		return nil
	})
	return files, err
}
  1. Using ioutil.ReadDir

> ioutil.ReadDir reads the directory named by dirname and returns a
> list of directory entries sorted by filename.

func IOReadDir(root string) ([]string, error) {
	var files []string
	fileInfo, err := ioutil.ReadDir(root)
	if err != nil {
		return files, err
	}

	for _, file := range fileInfo {
		files = append(files, file.Name())
	}
	return files, nil
}
  1. Using os.File.Readdir

> Readdir reads the contents of the directory associated with file and
> returns a slice of up to n FileInfo values, as would be returned by
> Lstat, in directory order. Subsequent calls on the same file will
> yield further FileInfos.

func OSReadDir(root string) ([]string, error) {
	var files []string
	f, err := os.Open(root)
	if err != nil {
		return files, err
	}
	fileInfo, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return files, err
	}

	for _, file := range fileInfo {
		files = append(files, file.Name())
	}
	return files, nil
}

Benchmark results.

在Go中列出目录

Get more details on this Blog Post

答案3

得分: 81

更简单的方法是使用path/filepath

package main    

import (
	"fmt"
    "log"
	"path/filepath"
)

func main() {
	files, err := filepath.Glob("*")
    if err != nil {
        log.Fatal(err)
    }
	fmt.Println(files) // 包含当前目录中所有文件的列表
}
英文:

Even simpler, use path/filepath:

package main    

import (
	&quot;fmt&quot;
    &quot;log&quot;
	&quot;path/filepath&quot;
)

func main() {
	files, err := filepath.Glob(&quot;*&quot;)
    if err != nil {
        log.Fatal(err)
    }
	fmt.Println(files) // contains a list of all files in the current directory
}

答案4

得分: 28

从Go 1.16开始,您可以使用os.ReadDir函数。

> func ReadDir(name string) ([]DirEntry, error)

它读取给定的目录并返回一个包含按文件名排序的目录条目的DirEntry切片。

这是一个乐观的函数,所以当在读取目录条目时发生错误时,它会尝试返回一个在错误之前的点之前的文件名切片。

package main

import (
	"fmt"
	"log"
	"os"
)

func main() {
	files, err := os.ReadDir(".")
	if err != nil {
		log.Fatal(err)
	}

	for _, file := range files {
		fmt.Println(file.Name())
	}
}

有趣的是:Go 1.17(2021年第三季度)包括fs.FileInfoToDirEntry()

>go &gt;func FileInfoToDirEntry(info FileInfo) DirEntry &gt;
>
> FileInfoToDirEntry返回一个返回来自info的信息的DirEntry
如果info为nil,则FileInfoToDirEntry返回nil。

英文:

Starting with Go 1.16, you can use the os.ReadDir function.

> func ReadDir(name string) ([]DirEntry, error)

It reads a given directory and returns a DirEntry slice that contains the directory entries sorted by filename.

It's an optimistic function, so that, when an error occurs while reading the directory entries, it tries to return you a slice with the filenames up to the point before the error.

package main

import (
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;os&quot;
)

func main() {
	files, err := os.ReadDir(&quot;.&quot;)
	if err != nil {
		log.Fatal(err)
	}

	for _, file := range files {
		fmt.Println(file.Name())
	}
}

Of interest: Go 1.17 (Q3 2021) includes fs.FileInfoToDirEntry():

>go
&gt;func FileInfoToDirEntry(info FileInfo) DirEntry
&gt;

>
> FileInfoToDirEntry returns a DirEntry that returns information from info.
If info is nil, FileInfoToDirEntry returns nil.


Background

Go 1.16 (Q1 2021) will propose, with CL 243908 and CL 243914 , the ReadDir function, based on the FS interface:

// An FS provides access to a hierarchical file system.
//
// The FS interface is the minimum implementation required of the file system.
// A file system may implement additional interfaces,
// such as fsutil.ReadFileFS, to provide additional or optimized functionality.
// See io/fsutil for details.
type FS interface {
	// Open opens the named file.
	//
	// When Open returns an error, it should be of type *PathError
	// with the Op field set to &quot;open&quot;, the Path field set to name,
	// and the Err field describing the problem.
	//
	// Open should reject attempts to open names that do not satisfy
	// ValidPath(name), returning a *PathError with Err set to
	// ErrInvalid or ErrNotExist.
	Open(name string) (File, error)
}

That allows for "os: add ReadDir method for lightweight directory reading":
See commit a4ede9f:

// ReadDir reads the contents of the directory associated with the file f
// and returns a slice of DirEntry values in directory order.
// Subsequent calls on the same file will yield later DirEntry records in the directory.
//
// If n &gt; 0, ReadDir returns at most n DirEntry records.
// In this case, if ReadDir returns an empty slice, it will return an error explaining why.
// At the end of a directory, the error is io.EOF.
//
// If n &lt;= 0, ReadDir returns all the DirEntry records remaining in the directory.
// When it succeeds, it returns a nil error (not io.EOF).
func (f *File) ReadDir(n int) ([]DirEntry, error) 

// A DirEntry is an entry read from a directory (using the ReadDir method).
type DirEntry interface {
	// Name returns the name of the file (or subdirectory) described by the entry.
	// This name is only the final element of the path, not the entire path.
	// For example, Name would return &quot;hello.go&quot; not &quot;/home/gopher/hello.go&quot;.
	Name() string
	
	// IsDir reports whether the entry describes a subdirectory.
	IsDir() bool
	
	// Type returns the type bits for the entry.
	// The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method.
	Type() os.FileMode
	
	// Info returns the FileInfo for the file or subdirectory described by the entry.
	// The returned FileInfo may be from the time of the original directory read
	// or from the time of the call to Info. If the file has been removed or renamed
	// since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist).
	// If the entry denotes a symbolic link, Info reports the information about the link itself,
	// not the link&#39;s target.
	Info() (FileInfo, error)
}

src/os/os_test.go#testReadDir() illustrates its usage:

	file, err := Open(dir)
	if err != nil {
		t.Fatalf(&quot;open %q failed: %v&quot;, dir, err)
	}
	defer file.Close()
	s, err2 := file.ReadDir(-1)
	if err2 != nil {
		t.Fatalf(&quot;ReadDir %q failed: %v&quot;, dir, err2)
	}

Ben Hoyt points out in the comments to Go 1.16 os.ReadDir:

> os.ReadDir(path string) ([]os.DirEntry, error), which you'll be able to call directly without the Open dance.
So you can probably shorten this to just os.ReadDir, as that's the concrete function most people will call.

See commit 3d913a9 (Dec. 2020):

> ## os: add ReadFile, WriteFile, CreateTemp (was TempFile), MkdirTemp (was TempDir) from io/ioutil
>
> io/ioutil was a poorly defined collection of helpers.
>
> Proposal #40025 moved out the generic I/O helpers to io.
This CL for proposal #42026 moves the OS-specific helpers to os,
making the entire io/ioutil package deprecated.

> os.ReadDir returns []DirEntry, in contrast to ioutil.ReadDir's []FileInfo.
(Providing a helper that returns []DirEntry is one of the primary motivations for this change.)

答案5

得分: 23

ioutil.ReadDir是一个很好的发现,但是如果你点击并查看源代码,你会发现它调用了os.File的Readdir方法。如果你对目录的顺序没有要求,也不需要对列表进行排序,那么这个Readdir方法就足够了。

英文:

ioutil.ReadDir is a good find, but if you click and look at the source you see that it calls the method Readdir of os.File. If you are okay with the directory order and don't need the list sorted, then this Readdir method is all you need.

答案6

得分: 18

根据您的描述,您可能想要使用os.Readdirnames

> func (f *File) Readdirnames(n int) (names []string, err error)
>
> Readdirnames函数读取与文件关联的目录的内容,并以目录顺序返回最多n个文件名的切片。对同一文件的后续调用将返回更多的文件名。
>
> ...
>
> 如果n <= 0,则Readdirnames函数将在一个切片中返回目录中的所有文件名。

代码片段:

file, err := os.Open(path)
if err != nil {
    return err
}
defer file.Close()
names, err := file.Readdirnames(0)
if err != nil {
    return err
}
fmt.Println(names)

感谢SquattingSlavInTracksuit评论;如果可以的话,我会建议将他们的评论提升为答案。

英文:

From your description, what you probably want is os.Readdirnames.

> func (f *File) Readdirnames(n int) (names []string, err error)
>
> Readdirnames reads the contents of the directory associated with file and returns a slice of up to n names of files in the directory, in directory order. Subsequent calls on the same file will yield further names.
>
> ...
>
> If n <= 0, Readdirnames returns all the names from the directory in a single slice.

Snippet:

file, err := os.Open(path)
if err != nil {
    return err
}
defer file.Close()
names, err := file.Readdirnames(0)
if err != nil {
    return err
}
fmt.Println(names)

Credit to SquattingSlavInTracksuit's comment; I'd have suggested promoting their comment to an answer if I could.

答案7

得分: 5

使用Readdirnames递归打印目录中的所有文件的完整示例

package main

import (
	"fmt"
	"os"
)

func main() {
	path := "/path/to/your/directory"
	err := readDir(path)
	if err != nil {
		panic(err)
	}
}

func readDir(path string) error {
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()
	names, _ := file.Readdirnames(0)
	for _, name := range names {
		filePath := fmt.Sprintf("%v/%v", path, name)
		file, err := os.Open(filePath)
		if err != nil {
			return err
		}
		defer file.Close()
		fileInfo, err := file.Stat()
		if err != nil {
			return err
		}
        fmt.Println(filePath)
		if fileInfo.IsDir() {
			readDir(filePath)
		}
	}
	return nil
}
英文:

A complete example of printing all the files in a directory recursively using Readdirnames

package main

import (
	&quot;fmt&quot;
	&quot;os&quot;
)

func main() {
	path := &quot;/path/to/your/directory&quot;
	err := readDir(path)
	if err != nil {
		panic(err)
	}
}

func readDir(path string) error {
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()
	names, _ := file.Readdirnames(0)
	for _, name := range names {
		filePath := fmt.Sprintf(&quot;%v/%v&quot;, path, name)
		file, err := os.Open(filePath)
		if err != nil {
			return err
		}
		defer file.Close()
		fileInfo, err := file.Stat()
		if err != nil {
			return err
		}
        fmt.Println(filePath)
		if fileInfo.IsDir() {
			readDir(filePath)
		}
	}
	return nil
}

huangapple
  • 本文由 发表于 2013年2月3日 10:17:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/14668850.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定