feat: 新增 sys.uptime/net.iface/net.conn/net.speed/proc.tree/file.empty/user.info 命令
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package executor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// runFileEmpty implements `kopsc file empty`: find empty files and directories.
|
||||
func runFileEmpty(ctx context.Context, req BuiltinRequest) error {
|
||||
opts, err := parseFileEmptyArgs(req.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
root, err := filepath.Abs(opts.path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法解析路径 %q: %w", opts.path, err)
|
||||
}
|
||||
|
||||
var emptyFiles []string
|
||||
var emptyDirs []string
|
||||
|
||||
err = filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return nil
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if d.IsDir() {
|
||||
// Check if directory is empty
|
||||
entries, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(entries) == 0 && path != root {
|
||||
rel, _ := filepath.Rel(root, path)
|
||||
if rel == "" {
|
||||
rel = path
|
||||
}
|
||||
emptyDirs = append(emptyDirs, rel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if file is empty
|
||||
info, err := d.Info()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if info.Size() == 0 {
|
||||
rel, _ := filepath.Rel(root, path)
|
||||
if rel == "" {
|
||||
rel = path
|
||||
}
|
||||
emptyFiles = append(emptyFiles, rel)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Strings(emptyFiles)
|
||||
sort.Strings(emptyDirs)
|
||||
|
||||
if !opts.dirsOnly {
|
||||
if len(emptyFiles) == 0 {
|
||||
fmt.Fprintln(req.Stdout, "没有空文件")
|
||||
} else {
|
||||
fmt.Fprintf(req.Stdout, "=== 空文件 (%d 个) ===\n", len(emptyFiles))
|
||||
for _, f := range emptyFiles {
|
||||
fmt.Fprintln(req.Stdout, f)
|
||||
}
|
||||
fmt.Fprintln(req.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.filesOnly {
|
||||
if len(emptyDirs) == 0 {
|
||||
fmt.Fprintln(req.Stdout, "没有空目录")
|
||||
} else {
|
||||
fmt.Fprintf(req.Stdout, "=== 空目录 (%d 个) ===\n", len(emptyDirs))
|
||||
for _, d := range emptyDirs {
|
||||
fmt.Fprintln(req.Stdout, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type fileEmptyOptions struct {
|
||||
path string
|
||||
filesOnly bool
|
||||
dirsOnly bool
|
||||
}
|
||||
|
||||
func parseFileEmptyArgs(args []string) (fileEmptyOptions, error) {
|
||||
opts := fileEmptyOptions{path: "."}
|
||||
var paths []string
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
switch {
|
||||
case arg == "--":
|
||||
paths = append(paths, args[i+1:]...)
|
||||
i = len(args)
|
||||
case arg == "--files-only":
|
||||
opts.filesOnly = true
|
||||
case arg == "--dirs-only":
|
||||
opts.dirsOnly = true
|
||||
case strings.HasPrefix(arg, "-") && arg != "-":
|
||||
return opts, fmt.Errorf("未知选项 %q", arg)
|
||||
default:
|
||||
paths = append(paths, arg)
|
||||
}
|
||||
}
|
||||
if len(paths) > 1 {
|
||||
return opts, fmt.Errorf("一次只支持一个路径,收到 %d 个", len(paths))
|
||||
}
|
||||
if len(paths) == 1 {
|
||||
opts.path = paths[0]
|
||||
}
|
||||
if opts.filesOnly && opts.dirsOnly {
|
||||
return opts, fmt.Errorf("--files-only 和 --dirs-only 不能同时使用")
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
Reference in New Issue
Block a user