Skip to content

Commit

Permalink
Support ls -R
Browse files Browse the repository at this point in the history
Fixes #100.
  • Loading branch information
Colm Dougan authored and colinmarc committed Jul 11, 2023
1 parent f5542d2 commit 03dbcde
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 29 deletions.
67 changes: 49 additions & 18 deletions cmd/hdfs/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/colinmarc/hdfs/v2"
)

func ls(paths []string, long, all, humanReadable bool) {
func ls(paths []string, long, all, humanReadable, recursive bool) {
paths, client, err := getClientAndExpandedPaths(paths)
if err != nil {
fatal(err)
Expand All @@ -25,7 +25,8 @@ func ls(paths []string, long, all, humanReadable bool) {

files := make([]string, 0, len(paths))
fileInfos := make([]os.FileInfo, 0, len(paths))
dirs := make([]string, 0, len(paths))

var dirs []string
for _, p := range paths {
fi, err := client.Stat(p)
if err != nil {
Expand All @@ -40,30 +41,60 @@ func ls(paths []string, long, all, humanReadable bool) {
}
}

// The target is a directory; print its contents instead of the directory
// entry itself. Even recursive ls on a single directory still just prints the
// toplevel first, without a leading "/foo/bar:".
skipTopLevel := false
if len(files) == 0 && len(dirs) == 1 {
printDir(client, dirs[0], long, all, humanReadable)
skipTopLevel = true
}

if long {
tw := lsTabWriter()
for i, p := range files {
printLong(tw, p, fileInfos[i], humanReadable)
}

tw.Flush()
} else {
if long {
tw := lsTabWriter()
for i, p := range files {
printLong(tw, p, fileInfos[i], humanReadable)
}
for _, p := range files {
fmt.Println(p)
}
}

tw.Flush()
} else {
for _, p := range files {
fmt.Println(p)
}
// Add all nested directories for the recursive case. These are printed as
// "siblings".
if recursive {
var nestedDirs []string
for _, dir := range dirs {
client.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

if info.IsDir() {
nestedDirs = append(nestedDirs, path)
}

return nil
})
}

for i, dir := range dirs {
if i > 0 || len(files) > 0 {
fmt.Println()
}
dirs = nestedDirs
}

if skipTopLevel && len(dirs) > 0 {
dirs = dirs[1:]
}

fmt.Printf("%s/:\n", dir)
printDir(client, dir, long, all, humanReadable)
for i, dir := range dirs {
if i > 0 || len(files) > 0 || skipTopLevel {
fmt.Println()
}

fmt.Printf("%s:\n", dir)
printDir(client, dir, long, all, humanReadable)
}
}

Expand Down
5 changes: 3 additions & 2 deletions cmd/hdfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var (
The flags available are a subset of the POSIX ones, but should behave similarly.
Valid commands:
ls [-lah] [FILE]...
ls [-lahR] [FILE]...
rm [-rf] FILE...
mv [-nT] SOURCE... DEST
mkdir [-p] FILE...
Expand All @@ -46,6 +46,7 @@ Valid commands:
lsl = lsOpts.Bool('l')
lsa = lsOpts.Bool('a')
lsh = lsOpts.Bool('h')
lsR = lsOpts.Bool('R')

rmOpts = getopt.New()
rmr = rmOpts.Bool('r')
Expand Down Expand Up @@ -118,7 +119,7 @@ func main() {
fatal("gohdfs version", version)
case "ls":
lsOpts.Parse(argv)
ls(lsOpts.Args(), *lsl, *lsa, *lsh)
ls(lsOpts.Args(), *lsl, *lsa, *lsh, *lsR)
case "rm":
rmOpts.Parse(argv)
rm(rmOpts.Args(), *rmr, *rmf)
Expand Down
50 changes: 42 additions & 8 deletions cmd/hdfs/test/glob.bats
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ setup() {
run $HDFS ls /_test_cmd/glob/dir*/dir
assert_success
assert_output <<OUT
/_test_cmd/glob/dir1/dir/:
/_test_cmd/glob/dir1/dir:
a
b
c
/_test_cmd/glob/dir2/dir/:
/_test_cmd/glob/dir2/dir:
d
OUT
}
Expand All @@ -33,12 +33,46 @@ OUT
assert_output <<OUT
/_test_cmd/glob/dir1/foo
/_test_cmd/glob/dir1/dir/:
/_test_cmd/glob/dir1/dir:
a
b
c
/_test_cmd/glob/dir2/dir/:
/_test_cmd/glob/dir2/dir:
d
OUT
}

@test "ls recursive on glo" {
run $HDFS ls -R /_test_cmd/glob/dir*
assert_success
assert_output <<OUT
/_test_cmd/glob/dir1:
dir
/_test_cmd/glob/dir1/dir:
a
b
c
/_test_cmd/glob/dir2:
dir
/_test_cmd/glob/dir2/dir:
d
OUT
}

@test "ls recursive on glob of leafs" {
run $HDFS ls -R /_test_cmd/glob/dir*/dir
assert_success
assert_output <<OUT
/_test_cmd/glob/dir1/dir:
a
b
c
/_test_cmd/glob/dir2/dir:
d
OUT
}
Expand All @@ -49,12 +83,12 @@ OUT
assert_output <<OUT
/_test_cmd/glob/dir1/foo
/_test_cmd/glob/dir1/dir/:
/_test_cmd/glob/dir1/dir:
a
b
c
/_test_cmd/glob/dir2/dir/:
/_test_cmd/glob/dir2/dir:
d
OUT
}
Expand All @@ -63,12 +97,12 @@ OUT
run $HDFS ls /_test_cmd/glob/dir*/dir*
assert_success
assert_output <<OUT
/_test_cmd/glob/dir1/dir/:
/_test_cmd/glob/dir1/dir:
a
b
c
/_test_cmd/glob/dir2/dir/:
/_test_cmd/glob/dir2/dir:
d
OUT
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/hdfs/test/helper.bash
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ flunk() {

assert_success() {
if [ "$status" -ne 0 ]; then
flunk "command failed with exit status $status"
flunk "command failed with exit status $status. output: $output"
elif [ "$#" -gt 0 ]; then
assert_output "$1"
fi
Expand Down
46 changes: 46 additions & 0 deletions cmd/hdfs/test/ls.bats
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,44 @@ c
OUT
}

@test "ls recursive on nested dir" {
run $HDFS ls -R /_test_cmd/ls
assert_success
assert_output <<OUT
dir1
dir2
dir3
/_test_cmd/ls/dir1:
a
b
c
/_test_cmd/ls/dir2:
d
/_test_cmd/ls/dir3:
OUT
}

@test "ls recursive on leaf dir" {
run $HDFS ls -R /_test_cmd/ls/dir1
assert_success
assert_output <<OUT
a
b
c
OUT
}

@test "ls recursive on file" {
run $HDFS ls -R /_test_cmd/ls/dir1/c
assert_success
assert_output <<OUT
/_test_cmd/ls/dir1/c
OUT
}

@test "ls single files" {
run $HDFS ls /_test_cmd/ls/dir1/a /_test_cmd/ls/dir1/b
assert_success
Expand All @@ -39,6 +77,14 @@ stat /_test_cmd/nonexistent: file does not exist
OUT
}

@test "ls recursive nonexistent" {
run $HDFS ls -R /_test_cmd/nonexistent
assert_failure
assert_output <<OUT
stat /_test_cmd/nonexistent: file does not exist
OUT
}

teardown() {
$HDFS rm -r /_test_cmd/ls
}

0 comments on commit 03dbcde

Please sign in to comment.