10.2 Solution

Let’s start small with an initial definition of printCatalogTree.

function printCatalogTree(path::Str, pad::Str)
    newPad::Str = pad * "   "
    for name in readdir(path)
        newPath::Str = joinpath(path, name)
        if isfile(newPath)
            println(newPad, name)
        else
            println(newPad, name, "/")
            printCatalogTree(newPath, newPad)
        end
    end
    return nothing
end

function printCatalogTree(path::Str)
    println(path, "/")
    printCatalogTree(path, "")
    return nothing
end

The function is rather simple. We walk through every entry (for name) in the examined directory (path). As we go name is converted to newPath which will be examined in a moment. If newPath is a file (isfile) we just print it, otherwise (else) it is a directory and we print it with "/" to make it stand out. Moreover we go inside of it with printCatalogTree (recursive call) to print its contents. For every nesting of printCatalogTree we update the padding newPad by increasing the indentation with * " ". We conclude with another version of printCatalogTree, the method will make the function’s invocation slightly easier and will add a header line for us (println(path, "/")) that will display the root directory (path).

Let’s see how it works (remember to create catalog_x with its contents first).

printCatalogTree(joinpath(homedir(), "Desktop", "catalog_x"))
/home/user_name/Desktop/catalog_x/
   catalog_y/
      catalog_z/
         file_z.txt
      file_y.txt
   file_x.txt

It looks quite alright. Time to replace the spaces on the left with some guideways that we may follow with our eyes.

function printCatalogTree(path::Str, pad::Str)
    newPad::Str = pad * "---"
    for name in readdir(path)
        newPath::Str = joinpath(path, name)
        if isfile(newPath)
            println(newPad, name)
        else
            println(newPad, name, "/")
            printCatalogTree(newPath, pad * "   |")
        end
    end
    return nothing
end

function printCatalogTree(path::Str)
    println(path, "/")
    printCatalogTree(path, "|")
    return nothing
end

To that end we changed the pads. We begin with the first column on the left that should start with the pipe character (printCatalogTree(path, "|")). Each line containing a file or directory should continue with hyphens “—” (newPad::Str = pad * "---"), whereas a nested directory and its contents should be indented and start with the pipe character as well (printCatalogTree(newPath, pad * " |")). Let’s see did it work as intended.

printCatalogTree(joinpath(homedir(), "Desktop", "catalog_x"))
/home/user_name/Desktop/catalog_x/
|---catalog_y/
|   |---catalog_z/
|   |   |---file_z.txt
|   |---file_y.txt
|---file_x.txt

Not bad at all. Time to add a summary line.

function printCatalogTree!(path::Str, pad::Str, count::Dict{Str, Int})
    newPad::Str = pad * "---"
    for name in readdir(path)
        newPath::Str = joinpath(path, name)
        if isfile(newPath)
            println(newPad, name)
            count["nFiles"] += 1
        else
            println(newPad, name, "/")
            count["nDirs"] += 1
            printCatalogTree!(newPath, pad * "   |", count)
        end
    end
    return nothing
end

function printCatalogTree(path::Str)
    println(path, "/")
    count::Dict{Str, Int}= Dict("nDirs" => 0, "nFiles" => 0)
    printCatalogTree!(path, "|", count)
    print("\n", count["nDirs"], " directories, ", count["nFiles"], " files")
    return nothing
end

The necessary data will be collected in a dictionary (count). It has two keys: nDirs and nFiles for the number of directories and files in the tree, respectively. Remember that in Julia dictionaries are passed by reference so any modification you make to them inside of a function will be visible outside. For that purpose count is defined in the outer printCatalogTree, whereas the inner printCatalogTree! got exclamation point per Julia’s convention to mark a function that modifies its arguments. Anyway, the count dictionary is updated for every file (count["nFiles"] += 1) and directory (count["nDirs"] += 1) encountered and a summary line is added at the very bottom of the output (print("\n", count["nDirs"], " directories, ", count["nFiles"], " files")). Let’s see how it works on a few examples (feel free to recreate mock directory structures as seen here).

First, a simple, already familiar to us, case.

printCatalogTree(joinpath(homedir(), "Desktop", "catalog_x"))
/home/user_name/Desktop/catalog_x/
|---catalog_y/
|   |---catalog_z/
|   |   |---file_z.txt
|   |---file_y.txt
|---file_x.txt

2 directories, 3 files

Now, a bit more convoluted tree.

printCatalogTree(joinpath(homedir(), "Desktop", "catalog_a"))
/home/user_name/Desktop/catalog_a/
|---catalog_b/
|   |---catalog_d/
|   |   |---file_d.txt
|   |---file_b1.txt
|   |---file_b2.txt
|---catalog_c/
|   |---catalog_e/
|   |   |---file_e.txt
|   |---catalog_f/
|   |   |---file_f1.txt
|   |   |---file_f2.txt
|   |   |---file_f3.txt
|   |   |---file_f4.txt
|---file_a.txt

5 directories, 9 files

And an empty directory.

printCatalogTree(joinpath(homedir(), "Desktop", "catalog_zzz"))
/home/user_name/Desktop/catalog_zzz/

0 directories, 0 files

OK, that’s it. Another tiny utility under our belts.



CC BY-NC-SA 4.0 Bartlomiej Lukaszuk