You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
2.9 KiB

6 years ago
6 years ago
6 years ago
  1. package shell
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/chrislusf/seaweedfs/weed/filer2"
  6. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  7. "io"
  8. "strings"
  9. )
  10. func init() {
  11. commands = append(commands, &commandFsTree{})
  12. }
  13. type commandFsTree struct {
  14. }
  15. func (c *commandFsTree) Name() string {
  16. return "fs.tree"
  17. }
  18. func (c *commandFsTree) Help() string {
  19. return `recursively list all files under a directory
  20. fs.tree http://<filer_server>:<port>/dir/
  21. `
  22. }
  23. func (c *commandFsTree) Do(args []string, commandEnv *commandEnv, writer io.Writer) (err error) {
  24. filerServer, filerPort, path, err := parseFilerUrl(args[len(args)-1])
  25. if err != nil {
  26. return err
  27. }
  28. dir, name := filer2.FullPath(path).DirAndName()
  29. if strings.HasSuffix(path, "/") {
  30. if path == "/" {
  31. dir, name = "/", ""
  32. } else {
  33. dir, name = path[0:len(path)-1], ""
  34. }
  35. }
  36. ctx := context.Background()
  37. return commandEnv.withFilerClient(ctx, filerServer, filerPort, func(client filer_pb.SeaweedFilerClient) error {
  38. return treeTraverseDirectory(ctx, writer, client, dir, name, 1000, newPrefix(), 0)
  39. })
  40. }
  41. func treeTraverseDirectory(ctx context.Context, writer io.Writer, client filer_pb.SeaweedFilerClient, dir, name string, paginateSize int, prefix *Prefix, level int) (err error) {
  42. paginatedCount := -1
  43. startFromFileName := ""
  44. for paginatedCount == -1 || paginatedCount == paginateSize {
  45. resp, listErr := client.ListEntries(ctx, &filer_pb.ListEntriesRequest{
  46. Directory: dir,
  47. Prefix: name,
  48. StartFromFileName: startFromFileName,
  49. InclusiveStartFrom: false,
  50. Limit: uint32(paginateSize),
  51. })
  52. if listErr != nil {
  53. err = listErr
  54. return
  55. }
  56. paginatedCount = len(resp.Entries)
  57. if paginatedCount > 0 {
  58. prefix.addMarker(level)
  59. }
  60. for i, entry := range resp.Entries {
  61. // 0.1% wrong prefix here, but fixing it would need to paginate to the next batch first
  62. isLast := paginatedCount < paginateSize && i == paginatedCount-1
  63. fmt.Fprintf(writer, "%s%s\n", prefix.getPrefix(level, isLast), entry.Name)
  64. if entry.IsDirectory {
  65. subDir := fmt.Sprintf("%s/%s", dir, entry.Name)
  66. if dir == "/" {
  67. subDir = "/" + entry.Name
  68. }
  69. err = treeTraverseDirectory(ctx, writer, client, subDir, "", paginateSize, prefix, level+1)
  70. } else {
  71. }
  72. startFromFileName = entry.Name
  73. }
  74. }
  75. return
  76. }
  77. type Prefix struct {
  78. markers map[int]bool
  79. }
  80. func newPrefix() *Prefix {
  81. return &Prefix{
  82. markers: make(map[int]bool),
  83. }
  84. }
  85. func (p *Prefix) addMarker(marker int) {
  86. p.markers[marker] = true
  87. }
  88. func (p *Prefix) removeMarker(marker int) {
  89. delete(p.markers, marker)
  90. }
  91. func (p *Prefix) getPrefix(level int, isLastChild bool) string {
  92. var sb strings.Builder
  93. for i := 0; i < level; i++ {
  94. if _, ok := p.markers[i]; ok {
  95. sb.WriteString("│")
  96. } else {
  97. sb.WriteString(" ")
  98. }
  99. sb.WriteString(" ")
  100. }
  101. if isLastChild {
  102. sb.WriteString("└──")
  103. p.removeMarker(level)
  104. } else {
  105. sb.WriteString("├──")
  106. }
  107. return sb.String()
  108. }