diff --git a/base/file.jl b/base/file.jl index 94f753d8b93d82..38237fe1b5cf68 100644 --- a/base/file.jl +++ b/base/file.jl @@ -260,3 +260,62 @@ function readdir(path::AbstractString) end readdir() = readdir(".") + +""" + walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw) + +The walkdir method return an iterator that walks the directory tree of a directory. The iterator returns a tuple containing +`(rootpath, dirs, files)`. The directory tree can be traversed top-down or bottom-up. If walkdir encounters a SystemError +it will raise the error. A custom error handling function can be provided through `onerror` keyword argument, the function +is called with a SystemError as argument. + + for (root, dirs, files) in walkdir(".") + println("Directories in \$root") + for dir in dirs + println(joinpath(root, dir)) # path to directories + end + println("Files in \$root") + for file in files + println(joinpath(root, file)) # path to files + end + end + +""" +function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) + content = nothing + try + content = readdir(root) + catch err + isa(SystemError) || throw(err) + onerror(err) + return + end + dirs = Array(eltype(content), 0) + files = Array(eltype(content), 0) + for name in content + if isdir(joinpath(root, name)) + push!(dirs, name) + else + push!(files, name) + end + end + + function _it() + if topdown + produce(root, dirs, files) + end + for dir in dirs + path = joinpath(root,dir) + if follow_symlinks || !islink(path) + for (root_l, dirs_l, files_l) in walkdir(path, topdown=topdown, follow_symlinks=follow_symlinks, onerror=onerror) + produce(root_l, dirs_l, files_l) + end + end + end + if !topdown + produce(root, dirs, files) + end + end + Task(_it) +end + diff --git a/test/file.jl b/test/file.jl index bd08ef3215f491..4aff56a8a6fce8 100644 --- a/test/file.jl +++ b/test/file.jl @@ -855,6 +855,86 @@ end @test_throws ArgumentError download("good", "ba\0d") @test_throws ArgumentError download("ba\0d", "good") +################### +# walkdir # +################### + +dir = mktempdir() +cd(dir) do + for i=1:2 + mkdir("sub_dir$i") + open("file$i", "w") do f end + + mkdir(joinpath("sub_dir1", "subsub_dir$i")) + open(joinpath("sub_dir1", "file$i"), "w") do f end + end + open(joinpath("sub_dir2", "file_dir2"), "w") + has_symlinks = @unix? true : (Base.windows_version() >= Base.WINDOWS_VISTA_VER) + follow_symlink_vec = has_symlinks ? [true, false] : [false] + has_symlinks && symlink("sub_dir2", joinpath("sub_dir1", "link")) + for follow_symlinks in follow_symlink_vec + task = walkdir(".", follow_symlinks=follow_symlinks) + root, dirs, files = consume(task) + @test root == "." + @test dirs == ["sub_dir1", "sub_dir2"] + @test files == ["file1", "file2"] + + root, dirs, files = consume(task) + @test root == joinpath(".", "sub_dir1") + @test dirs == (has_symlinks ? ["link", "subsub_dir1", "subsub_dir2"] : ["subsub_dir1", "subsub_dir2"]) + @test files == ["file1", "file2"] + + root, dirs, files = consume(task) + if follow_symlinks + @test root == joinpath(".", "sub_dir1", "link") + @test dirs == [] + @test files == ["file_dir2"] + root, dirs, files = consume(task) + end + for i=1:2 + @test root == joinpath(".", "sub_dir1", "subsub_dir$i") + @test dirs == [] + @test files == [] + root, dirs, files = consume(task) + end + + @test root == joinpath(".", "sub_dir2") + @test dirs == [] + @test files == ["file_dir2"] + end + + for follow_symlinks in follow_symlink_vec + task = walkdir(".", follow_symlinks=follow_symlinks, topdown=false) + root, dirs, files = consume(task) + if follow_symlinks + @test root == joinpath(".", "sub_dir1", "link") + @test dirs == [] + @test files == ["file_dir2"] + root, dirs, files = consume(task) + end + for i=1:2 + @test root == joinpath(".", "sub_dir1", "subsub_dir$i") + @test dirs == [] + @test files == [] + root, dirs, files = consume(task) + end + @test root == joinpath(".", "sub_dir1") + @test dirs == (has_symlinks ? ["link", "subsub_dir1", "subsub_dir2"] : ["subsub_dir1", "subsub_dir2"]) + @test files == ["file1", "file2"] + + root, dirs, files = consume(task) + @test root == joinpath(".", "sub_dir2") + @test dirs == [] + @test files == ["file_dir2"] + + root, dirs, files = consume(task) + @test root == "." + @test dirs == ["sub_dir1", "sub_dir2"] + @test files == ["file1", "file2"] + end +end +rm(dir, recursive=true) + ############ # Clean up # ############