0
# Directory Operations
1
2
Traverse directory structures with filtering, sorting, and recursion options. Support for file type filtering, name matching, and advanced traversal patterns with comprehensive closure-based processing.
3
4
## Capabilities
5
6
### Basic Directory Iteration
7
8
Iterate through files and directories in a directory with type filtering.
9
10
```java { .api }
11
/**
12
* Invokes the closure for each 'child' file in this 'parent' folder/directory.
13
* Both regular files and subfolders/subdirectories are processed
14
* @param self a Path (that happens to be a folder/directory)
15
* @param closure a closure (the parameter is the Path for the 'child' file)
16
* @throws FileNotFoundException if the given directory does not exist
17
* @throws IllegalArgumentException if the provided Path object does not represent a directory
18
*/
19
void eachFile(Path self, Closure closure);
20
21
/**
22
* Invokes the closure for each 'child' file in this 'parent' folder/directory.
23
* Both regular files and subfolders/subdirectories can be processed depending on the fileType enum value
24
* @param self a Path (that happens to be a folder/directory)
25
* @param fileType if normal files or directories or both should be processed
26
* @param closure the closure to invoke
27
* @throws FileNotFoundException if the given directory does not exist
28
* @throws IllegalArgumentException if the provided Path object does not represent a directory
29
*/
30
void eachFile(Path self, FileType fileType, Closure closure);
31
32
/**
33
* Invokes the closure for each subdirectory in this directory, ignoring regular files
34
* @param self a Path (that happens to be a folder/directory)
35
* @param closure a closure (the parameter is the Path for the subdirectory file)
36
* @throws FileNotFoundException if the given directory does not exist
37
* @throws IllegalArgumentException if the provided Path object does not represent a directory
38
*/
39
void eachDir(Path self, Closure closure);
40
```
41
42
**Usage Examples:**
43
44
```groovy
45
import java.nio.file.Path
46
import java.nio.file.Paths
47
import groovy.io.FileType
48
49
Path directory = Paths.get("/home/user/documents")
50
51
// Process all files and directories
52
directory.eachFile { file ->
53
println "${file.fileName} - ${Files.isDirectory(file) ? 'DIR' : 'FILE'}"
54
}
55
56
// Process only regular files
57
directory.eachFile(FileType.FILES) { file ->
58
println "File: ${file.fileName} (${file.size()} bytes)"
59
}
60
61
// Process only directories
62
directory.eachFile(FileType.DIRECTORIES) { dir ->
63
println "Directory: ${dir.fileName}"
64
}
65
// or use the convenience method
66
directory.eachDir { dir ->
67
println "Directory: ${dir.fileName}"
68
}
69
70
// Process with file type checking
71
directory.eachFile { path ->
72
if (Files.isRegularFile(path)) {
73
println "File: ${path.fileName}"
74
} else if (Files.isDirectory(path)) {
75
println "Directory: ${path.fileName}/"
76
}
77
}
78
```
79
80
### Recursive Directory Traversal
81
82
Recursively traverse directory trees with type filtering and processing options.
83
84
```java { .api }
85
/**
86
* Processes each descendant file in this directory and any sub-directories.
87
* Processing consists of calling closure passing it the current file (which may be a normal file or subdirectory)
88
* and then if a subdirectory was encountered, recursively processing the subdirectory
89
* @param self a Path (that happens to be a folder/directory)
90
* @param closure a Closure
91
* @throws FileNotFoundException if the given directory does not exist
92
* @throws IllegalArgumentException if the provided Path object does not represent a directory
93
*/
94
void eachFileRecurse(Path self, Closure closure);
95
96
/**
97
* Processes each descendant file in this directory and any sub-directories.
98
* Processing consists of potentially calling closure passing it the current file and then if a subdirectory was encountered,
99
* recursively processing the subdirectory. Whether the closure is called is determined by whether
100
* the file was a normal file or subdirectory and the value of fileType
101
* @param self a Path (that happens to be a folder/directory)
102
* @param fileType if normal files or directories or both should be processed
103
* @param closure the closure to invoke on each file
104
* @throws FileNotFoundException if the given directory does not exist
105
* @throws IllegalArgumentException if the provided Path object does not represent a directory
106
*/
107
void eachFileRecurse(Path self, FileType fileType, Closure closure);
108
109
/**
110
* Recursively processes each descendant subdirectory in this directory.
111
* Processing consists of calling closure passing it the current subdirectory and then recursively processing that subdirectory.
112
* Regular files are ignored during traversal
113
* @param self a Path (that happens to be a folder/directory)
114
* @param closure a closure
115
* @throws FileNotFoundException if the given directory does not exist
116
* @throws IllegalArgumentException if the provided Path object does not represent a directory
117
*/
118
void eachDirRecurse(Path self, Closure closure);
119
```
120
121
**Usage Examples:**
122
123
```groovy
124
import java.nio.file.Path
125
import java.nio.file.Paths
126
import groovy.io.FileType
127
128
Path rootDir = Paths.get("/project")
129
130
// Recursively process all files and directories
131
rootDir.eachFileRecurse { file ->
132
println file.toString()
133
}
134
135
// Recursively process only regular files
136
rootDir.eachFileRecurse(FileType.FILES) { file ->
137
if (file.fileName.toString().endsWith(".java")) {
138
println "Java file: ${file}"
139
}
140
}
141
142
// Recursively process only directories
143
rootDir.eachFileRecurse(FileType.DIRECTORIES) { dir ->
144
println "Directory: ${dir}"
145
}
146
// or use the convenience method
147
rootDir.eachDirRecurse { dir ->
148
println "Directory: ${dir}"
149
}
150
151
// Count files by extension
152
Map<String, Integer> extensionCounts = [:]
153
rootDir.eachFileRecurse(FileType.FILES) { file ->
154
String filename = file.fileName.toString()
155
int lastDot = filename.lastIndexOf('.')
156
String extension = lastDot > 0 ? filename.substring(lastDot) : "(no extension)"
157
extensionCounts[extension] = (extensionCounts[extension] ?: 0) + 1
158
}
159
extensionCounts.each { ext, count ->
160
println "${ext}: ${count} files"
161
}
162
```
163
164
### Advanced Directory Traversal
165
166
Advanced traversal with extensive configuration options including filtering, sorting, depth control, and pre/post processing.
167
168
```java { .api }
169
/**
170
* Processes each descendant file in this directory and any sub-directories.
171
* Convenience method for traverse(Path, Map, Closure) when no options to alter the traversal behavior are required
172
* @param self a Path (that happens to be a folder/directory)
173
* @param closure the Closure to invoke on each file/directory and optionally returning a FileVisitResult value
174
* which can be used to control subsequent processing
175
* @throws FileNotFoundException if the given directory does not exist
176
* @throws IllegalArgumentException if the provided Path object does not represent a directory
177
*/
178
void traverse(Path self, Closure closure);
179
180
/**
181
* Processes each descendant file in this directory and any sub-directories.
182
* The traversal can be adapted by providing various options in the options Map
183
* @param self a Path (that happens to be a folder/directory)
184
* @param options a Map of options to alter the traversal behavior
185
* @param closure the Closure to invoke on each file/directory and optionally returning a FileVisitResult value
186
* which can be used to control subsequent processing
187
* @throws FileNotFoundException if the given directory does not exist
188
* @throws IllegalArgumentException if the provided Path object does not represent a directory or illegal filter combinations are supplied
189
*/
190
void traverse(Path self, Map<String, Object> options, Closure closure);
191
192
/**
193
* Invokes the closure specified with key 'visit' in the options Map
194
* for each descendant file in this directory tree. Convenience method
195
* for traverse(Path, Map, Closure) allowing the 'visit' closure
196
* to be included in the options Map rather than as a parameter
197
* @param self a Path (that happens to be a folder/directory)
198
* @param options a Map of options to alter the traversal behavior
199
* @throws FileNotFoundException if the given directory does not exist
200
* @throws IllegalArgumentException if the provided Path object does not represent a directory or illegal filter combinations are supplied
201
*/
202
void traverse(Path self, Map<String, Object> options);
203
```
204
205
**Traversal Options:**
206
207
- `type`: A `FileType` enum to determine if normal files or directories or both are processed
208
- `preDir`: A closure run before each directory is processed, optionally returning a `FileVisitResult` value
209
- `preRoot`: A boolean indicating that the 'preDir' closure should be applied at the root level
210
- `postDir`: A closure run after each directory is processed, optionally returning a `FileVisitResult` value
211
- `postRoot`: A boolean indicating that the 'postDir' closure should be applied at the root level
212
- `visitRoot`: A boolean indicating that the given closure should be applied for the root dir
213
- `maxDepth`: The maximum number of directory levels when recursing (default is -1 which means infinite, set to 0 for no recursion)
214
- `filter`: A filter to perform on traversed files/directories. If set, only files/dirs which match are candidates for visiting
215
- `nameFilter`: A filter to perform on the name of traversed files/directories. If set, only files/dirs which match are candidates for visiting. (Must not be set if 'filter' is set)
216
- `excludeFilter`: A filter to perform on traversed files/directories. If set, any candidates which match won't be visited
217
- `excludeNameFilter`: A filter to perform on the names of traversed files/directories. If set, any candidates which match won't be visited. (Must not be set if 'excludeFilter' is set)
218
- `sort`: A closure which if set causes the files and subdirectories for each directory to be processed in sorted order
219
220
**Usage Examples:**
221
222
```groovy
223
import java.nio.file.Path
224
import java.nio.file.Paths
225
import groovy.io.FileType
226
import groovy.io.FileVisitResult
227
228
Path projectDir = Paths.get("/project")
229
230
// Simple traversal
231
projectDir.traverse { file ->
232
println file.toString()
233
}
234
235
// Advanced traversal with options
236
def totalSize = 0
237
def count = 0
238
239
projectDir.traverse([
240
type: FileType.FILES,
241
nameFilter: ~/.*\.groovy$/,
242
maxDepth: 3,
243
preDir: { dir ->
244
println "Entering directory: ${dir.fileName}"
245
if (dir.fileName.toString() == '.git') {
246
return FileVisitResult.SKIP_SUBTREE
247
}
248
},
249
postDir: { dir ->
250
println "Exiting directory: ${dir.fileName}"
251
},
252
sort: { a, b ->
253
// Sort by type (directories first), then by name
254
if (Files.isDirectory(a) != Files.isDirectory(b)) {
255
return Files.isDirectory(a) ? -1 : 1
256
}
257
return a.fileName.toString().compareTo(b.fileName.toString())
258
}
259
]) { file ->
260
totalSize += file.size()
261
count++
262
println "Groovy file: ${file} (${file.size()} bytes)"
263
}
264
265
println "Found ${count} Groovy files totaling ${totalSize} bytes"
266
267
// Using visit closure in options
268
projectDir.traverse([
269
type: FileType.FILES,
270
excludeNameFilter: ~/\.(class|jar)$/,
271
visit: { file ->
272
println "Processing: ${file}"
273
}
274
])
275
276
// Control traversal flow with FileVisitResult
277
projectDir.traverse([
278
type: FileType.FILES,
279
nameFilter: ~/.*\.txt$/
280
]) { file ->
281
if (file.size() > 1024 * 1024) { // Files larger than 1MB
282
println "Large file found: ${file}"
283
return FileVisitResult.TERMINATE // Stop traversal
284
}
285
println "Text file: ${file}"
286
return FileVisitResult.CONTINUE
287
}
288
```
289
290
### File Name Matching
291
292
Match files and directories based on name patterns with various filtering options.
293
294
```java { .api }
295
/**
296
* Invokes the closure for each file whose name matches the given nameFilter in the given directory
297
* Both regular files and subdirectories are matched
298
* @param self a Path (that happens to be a folder/directory)
299
* @param nameFilter the nameFilter to perform on the name of the file
300
* @param closure the closure to invoke
301
* @throws FileNotFoundException if the given directory does not exist
302
* @throws IllegalArgumentException if the provided Path object does not represent a directory
303
*/
304
void eachFileMatch(Path self, Object nameFilter, Closure closure);
305
306
/**
307
* Invokes the closure for each file whose name matches the given nameFilter in the given directory
308
* Both regular files and subdirectories may be candidates for matching depending on the value of fileType
309
* @param self a Path (that happens to be a folder/directory)
310
* @param fileType whether normal files or directories or both should be processed
311
* @param nameFilter the filter to perform on the name of the file/directory
312
* @param closure the closure to invoke
313
* @throws FileNotFoundException if the given directory does not exist
314
* @throws IllegalArgumentException if the provided Path object does not represent a directory
315
*/
316
void eachFileMatch(Path self, FileType fileType, Object nameFilter, Closure closure);
317
318
/**
319
* Invokes the closure for each subdirectory whose name matches the given nameFilter in the given directory
320
* Only subdirectories are matched; regular files are ignored
321
* @param self a Path (that happens to be a folder/directory)
322
* @param nameFilter the nameFilter to perform on the name of the directory
323
* @param closure the closure to invoke
324
* @throws FileNotFoundException if the given directory does not exist
325
* @throws IllegalArgumentException if the provided Path object does not represent a directory
326
*/
327
void eachDirMatch(Path self, Object nameFilter, Closure closure);
328
```
329
330
**Usage Examples:**
331
332
```groovy
333
import java.nio.file.Path
334
import java.nio.file.Paths
335
import groovy.io.FileType
336
337
Path directory = Paths.get("/project/src")
338
339
// Match files with regex pattern
340
directory.eachFileMatch(~/.*\.java$/) { file ->
341
println "Java file: ${file.fileName}"
342
}
343
344
// Match only directories with pattern
345
directory.eachFileMatch(FileType.DIRECTORIES, ~/test.*/) { dir ->
346
println "Test directory: ${dir.fileName}"
347
}
348
349
// Use convenience method for directories
350
directory.eachDirMatch(~/lib.*/) { dir ->
351
println "Library directory: ${dir.fileName}"
352
}
353
354
// Match with closure filter
355
directory.eachFileMatch({ fileName ->
356
fileName.length() > 10 && fileName.endsWith(".groovy")
357
}) { file ->
358
println "Long Groovy file: ${file.fileName}"
359
}
360
361
// Complex pattern matching
362
directory.eachFileMatch(FileType.FILES, ~/.*\.(java|groovy|scala)$/) { file ->
363
def lang = file.fileName.toString().split('\\.').last()
364
println "${lang.toUpperCase()} source: ${file.fileName}"
365
}
366
367
// Match backup files for deletion
368
directory.eachFileMatch(~/.*\.bak$/) { backup ->
369
println "Deleting backup: ${backup.fileName}"
370
Files.delete(backup)
371
}
372
```
373
374
### Directory Management
375
376
Operations for managing directories including deletion and renaming.
377
378
```java { .api }
379
/**
380
* Deletes a directory with all contained files and subdirectories
381
* @param self a Path
382
* @return true if the file doesn't exist or deletion was successful, false otherwise
383
*/
384
boolean deleteDir(Path self);
385
386
/**
387
* Renames a file
388
* @param self a Path
389
* @param newPathName The new pathname for the named file
390
* @return true if and only if the renaming succeeded; false otherwise
391
*/
392
boolean renameTo(Path self, String newPathName);
393
394
/**
395
* Renames a file
396
* @param self a Path
397
* @param newPathName The new target path specified as a URI object
398
* @return true if and only if the renaming succeeded; false otherwise
399
*/
400
boolean renameTo(Path self, URI newPathName);
401
```
402
403
**Usage Examples:**
404
405
```groovy
406
import java.nio.file.Path
407
import java.nio.file.Paths
408
409
Path tempDir = Paths.get("/tmp/my-temp-dir")
410
Path oldName = Paths.get("/project/old-name")
411
Path targetDir = Paths.get("/project/archive")
412
413
// Delete directory and all contents
414
if (tempDir.deleteDir()) {
415
println "Successfully deleted ${tempDir}"
416
} else {
417
println "Failed to delete ${tempDir}"
418
}
419
420
// Rename directory
421
if (oldName.renameTo("/project/new-name")) {
422
println "Successfully renamed directory"
423
}
424
425
// Rename using URI
426
URI newLocation = new URI("file:///project/renamed-dir")
427
if (oldName.renameTo(newLocation)) {
428
println "Successfully moved to new location"
429
}
430
431
// Safe directory cleanup
432
Path backupDir = Paths.get("/backups/old")
433
if (Files.exists(backupDir)) {
434
if (backupDir.deleteDir()) {
435
println "Old backup directory cleaned up"
436
} else {
437
println "Warning: Could not delete backup directory"
438
}
439
}
440
```