github.com/sandwich-go/boost@v1.3.29/xos/dir.go (about)

     1  package xos
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/sandwich-go/boost"
     6  	"github.com/sandwich-go/boost/xerror"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  
    12  	"github.com/sandwich-go/boost/xslice"
    13  )
    14  
    15  // MkdirAll 创建 path 的目录
    16  func MkdirAll(path string) error {
    17  	return Mkdir(filepath.Dir(path))
    18  }
    19  
    20  // Mkdir 创建目录
    21  func Mkdir(dir string) error {
    22  	if ExistsFile(dir) {
    23  		return nil
    24  	}
    25  	return os.MkdirAll(dir, 0775)
    26  }
    27  
    28  // IsEmpty 检测目录是否为空,如目录下存在隐藏文件也会认为是非空目录
    29  func IsEmpty(path string) bool {
    30  	stat, err := os.Stat(path)
    31  	if err != nil {
    32  		return true
    33  	}
    34  	if stat.IsDir() {
    35  		file, err0 := os.Open(path)
    36  		if err0 != nil {
    37  			return true
    38  		}
    39  		defer func() {
    40  			_ = file.Close()
    41  		}()
    42  		names, err1 := file.Readdirnames(-1)
    43  		if err1 != nil {
    44  			return true
    45  		}
    46  		return len(names) == 0
    47  	} else {
    48  		return stat.Size() == 0
    49  	}
    50  }
    51  
    52  // RemoveSubDirsUnderDir 删除指定目录下的子目录
    53  func RemoveSubDirsUnderDir(dir string, filter func(dir string) bool) error {
    54  	if !ExistsDir(dir) {
    55  		return nil
    56  	}
    57  	fs, err := ioutil.ReadDir(dir)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	fileList := make([]string, 0)
    62  	for _, f := range fs {
    63  		if f.IsDir() {
    64  			fd := filepath.Join(dir, f.Name())
    65  			if filter == nil || filter(fd) {
    66  				fileList = append(fileList, fd)
    67  			}
    68  		}
    69  	}
    70  	for _, filePath := range fileList {
    71  		subFileList := make([]string, 0)
    72  		_ = filepath.Walk(filePath, FileWalkFunc(&subFileList))
    73  		// 排序,是为了先删除文件,再删除空目录
    74  		sort.Sort(sort.Reverse(sort.StringSlice(subFileList)))
    75  		for _, subFilePath := range subFileList {
    76  			_ = os.Remove(subFilePath)
    77  		}
    78  	}
    79  	return nil
    80  }
    81  
    82  func removeEmptyDir(path string) error {
    83  	files, err := ioutil.ReadDir(path)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	if len(files) != 0 {
    88  		return fmt.Errorf("not empty dir, %s", path)
    89  	}
    90  	return os.Remove(path)
    91  }
    92  
    93  // RemoveEmptyDirs 删除空目录
    94  // 若目录不为空,则报错
    95  func RemoveEmptyDirs(root string) error {
    96  	return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
    97  		if err != nil {
    98  			return err
    99  		}
   100  		if info.IsDir() {
   101  			err = removeEmptyDir(path)
   102  		}
   103  		return err
   104  	})
   105  }
   106  
   107  // RemoveDirs 删除指定目录下所有内容
   108  func RemoveDirs(dir string) error {
   109  	d, err := os.Open(dir)
   110  	if err != nil {
   111  		return err
   112  	}
   113  	defer func() {
   114  		_ = d.Close()
   115  	}()
   116  	names, err0 := d.Readdirnames(-1)
   117  	if err0 != nil {
   118  		return err0
   119  	}
   120  	for _, name := range names {
   121  		err = os.RemoveAll(filepath.Join(dir, name))
   122  		if err != nil {
   123  			return err
   124  		}
   125  	}
   126  	return nil
   127  }
   128  
   129  // RemoveFilesUnderDir 删除目录下的文件
   130  func RemoveFilesUnderDir(pathStr string, filter func(filePath string) bool) {
   131  	fileList := make([]string, 0)
   132  	_ = filepath.Walk(pathStr, FileWalkFuncWithIncludeFilter(&fileList, filter))
   133  	for _, filePath := range fileList {
   134  		_ = os.Remove(filePath)
   135  	}
   136  }
   137  
   138  // FileWalkFunc 只有指定的ext为合法文件
   139  func FileWalkFunc(files *[]string, ext ...string) filepath.WalkFunc {
   140  	return func(path string, info os.FileInfo, err error) error {
   141  		if len(ext) > 0 {
   142  			if xslice.StringsContain(ext, filepath.Ext(path)) {
   143  				*files = append(*files, path)
   144  			}
   145  		} else {
   146  			*files = append(*files, path)
   147  		}
   148  		return err
   149  	}
   150  }
   151  
   152  // FileWalkFuncWithIncludeFilter  filepath.WalkFunc 通过include过滤合法的文件,如指定了ext则只有ext扩展类型的文件合法
   153  func FileWalkFuncWithIncludeFilter(files *[]string, include func(f string) bool, ext ...string) filepath.WalkFunc {
   154  	return func(path string, info os.FileInfo, err error) error {
   155  		if err != nil {
   156  			return err
   157  		}
   158  		if info.IsDir() {
   159  			return err
   160  		}
   161  		if include == nil || include(path) {
   162  			if len(ext) > 0 {
   163  				if xslice.StringsContain(ext, filepath.Ext(path)) {
   164  					*files = append(*files, path)
   165  				}
   166  			} else {
   167  				*files = append(*files, path)
   168  			}
   169  		}
   170  		return err
   171  	}
   172  }
   173  
   174  // FileWalkFuncWithExcludeFilter filepath.WalkFunc 通过excluded过滤不合法文件,如指定了ext则只有ext扩展类型的文件合法
   175  func FileWalkFuncWithExcludeFilter(files *[]string, excluded func(f string) bool, ext ...string) filepath.WalkFunc {
   176  	return func(path string, info os.FileInfo, err error) error {
   177  		if excluded != nil && excluded(path) {
   178  			return err
   179  		}
   180  		if len(ext) > 0 {
   181  			if xslice.StringsContain(ext, filepath.Ext(path)) {
   182  				*files = append(*files, path)
   183  			}
   184  		} else {
   185  			*files = append(*files, path)
   186  		}
   187  		return err
   188  	}
   189  }
   190  
   191  // FilePathWalkFollowLink 遍历目录
   192  func FilePathWalkFollowLink(root string, walkFn filepath.WalkFunc) error {
   193  	dir, err := GetActuallyDir(root)
   194  	if err != nil {
   195  		return err
   196  	}
   197  	return filepath.Walk(dir, walkFn)
   198  }
   199  
   200  // GetActuallyDir 获取真实目录,如果root为一个link则寻找link的真实目录
   201  func GetActuallyDir(root string) (string, error) {
   202  	dirInfo, err := os.Lstat(root)
   203  	if err != nil {
   204  		return root, err
   205  	}
   206  	if dirInfo.Mode()&os.ModeSymlink != 0 {
   207  		dirLinkTo, err0 := os.Readlink(root)
   208  		if err0 != nil {
   209  			return root, err0
   210  		}
   211  		return dirLinkTo, nil
   212  	}
   213  	return root, nil
   214  }
   215  
   216  // ReadDir returns the filenames in the given directory in sorted order.
   217  func ReadDir(root string) ([]string, error) {
   218  	return ReadDirWithExt(root, "")
   219  }
   220  
   221  // ReadDirWithExt returns the filenames in the given directory in sorted order.
   222  func ReadDirWithExt(root, ext string) ([]string, error) {
   223  	dir, err := os.Open(root)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	defer func() {
   228  		boost.LogErrorAndEatError(dir.Close())
   229  	}()
   230  	names, err0 := dir.Readdirnames(-1)
   231  	if err0 != nil {
   232  		return nil, err0
   233  	}
   234  	sort.Strings(names)
   235  
   236  	if len(ext) > 0 {
   237  		tss := make([]string, 0, len(names))
   238  		for _, v := range names {
   239  			if filepath.Ext(v) == ext {
   240  				tss = append(tss, v)
   241  			}
   242  		}
   243  		names = tss
   244  	}
   245  	return names, nil
   246  }
   247  
   248  // IsDirWriteable checks if dir is writable by writing and removing a file
   249  // to dir. It returns nil if dir is writable.
   250  func IsDirWriteable(dir string) error {
   251  	f := filepath.Join(dir, ".touch")
   252  	if err := ioutil.WriteFile(f, []byte(""), 0600); err != nil {
   253  		return err
   254  	}
   255  	return os.Remove(f)
   256  }
   257  
   258  // TouchDirAll is similar to os.MkdirAll. It creates directories with 0700 permission if any directory
   259  // does not exists. TouchDirAll also ensures the given directory is writable.
   260  func TouchDirAll(dir string) error {
   261  	// If path is already a directory, MkdirAll does nothing
   262  	// and returns nil.
   263  	err := os.MkdirAll(dir, 0700)
   264  	if err != nil {
   265  		// if mkdirAll("a/text") and "text" is not
   266  		// a directory, this will return syscall.ENOTDIR
   267  		return err
   268  	}
   269  	return IsDirWriteable(dir)
   270  }
   271  
   272  // CreateDirAll is similar to TouchDirAll but returns error
   273  // if the deepest directory was not empty.
   274  func CreateDirAll(dir string) error {
   275  	err := TouchDirAll(dir)
   276  	if err == nil {
   277  		var ns []string
   278  		ns, err = ReadDir(dir)
   279  		if err != nil {
   280  			return err
   281  		}
   282  		if len(ns) != 0 {
   283  			err = xerror.NewText("expected %q to be empty, got %q", dir, ns)
   284  		}
   285  	}
   286  	return err
   287  }