Hierarchical file systems are the way we store most of our data. Groovy can help to build code that needs to go through a dense forest of directory trees.
In this recipe, we will cover different ways of walking through a directory tree using Groovy I/O awesomeness.
Let's assume that we need to walk through the current working directory. We can define the currentDir
variable of java.io.File
type that points to it:
def currentDir = new File('.')
To test this recipe, you can use either a script file that you launch with the groovy
command or the groovysh
/groovyConsole
prompt.
As you probably know, the java.io.File
class already provides the
list
and listFiles
methods that return a collection of first-level elements (files and directories) in a directory represented by a File
object. Using a recursive function, you can easily traverse the subfolders found in the first-level folders.
eachFileRecurse
method. It takes a closure as an input parameter, which is called for every file or directory entry that is found:currentDir.eachFileRecurse { File file -> println file.name }
eachDirRecurse
method instead:currentDir.eachDirRecurse { File dir -> println dir.name }
eachFileRecurse
, which is a value of a groovy.io.FileType
enumeration (you'll need to add an additional import at the beginning of your script or groovysh/groovyConsole
prompt):import groovy.io.FileType ... currentDir.eachFileRecurse(FileType.FILES) { File file -> println file.name }
The previous code recursively prints the names of all the files and/or directories in the current directory tree. The recursive walk performs a depth-first search. That means that when the search goes through a list of children and encounters a directory, then it goes (deeper) inside that directory first before returning to process the remaining children. The order in which files and directories are processed depends on the implementation of the file system.
Groovy also adds the eachFile
and eachDir
methods to a java.io.File
class that you can use to iterate through the first-level elements only (just like listFiles
). As other each-methods, they take a closure as an input parameter. You can use that to build a recursive function in a similar way as you would use it with the listFiles
method. But instead of defining a separate function, you can also make use of Groovy's closure that is capable of creating a copy of itself with the help of the trampoline
method:
currentDir.eachFile { File file -> println file.name if (file.isDirectory()) { file.eachFile( trampoline() ) } }
The code snippet above does exactly the same thing as the previous code examples. The "magic" trampoline
method passes a copy of the parent closure to the nested eachFile
call, a copy of the parent closure and makes it behave like a recursive function.
The previous example is very condensed, but it's not as obvious and easy to read compared to the initially described methods.
Since v1.7.1, Groovy has also introduced a very powerful traverse
method in the java.io.File
class. In its simplest form, it does the same as eachFileRecurse
:
currentDir.traverse { File file -> println file.name }
The traverse
method has an overloaded version that also accepts a Map
of additional parameters, which allows you to control various aspects of directory traversing behavior.
More advanced traverse options are described in the Searching for files recipe.
The following recipes contain some additional information on the topics discussed in this recipe:
Also, it's worth examining the Groovydoc for the File
, FileType
, and Closure
classes: