or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

directory-operations.mdfile-appending.mdfile-io.mdindex.mdline-processing.mdobject-serialization.mdstream-operations.md

directory-operations.mddocs/

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

```