or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-transforms.mdcli.mdcore-language.mdindex.mdjson.mdsql.mdswing.mdtemplates.mdxml.md

cli.mddocs/

0

# Command Line Interface

1

2

Groovy provides powerful command-line argument parsing capabilities through CliBuilder, which integrates with PicoCLI for creating robust CLI applications.

3

4

## CliBuilder

5

6

### Basic CLI Builder

7

8

```groovy { .api }

9

class CliBuilder {

10

CliBuilder()

11

CliBuilder(String usage)

12

CliBuilder(PrintWriter writer)

13

CliBuilder(PrintWriter writer, String usage)

14

15

void setUsage(String usage)

16

String getUsage()

17

void setHeader(String header)

18

void setFooter(String footer)

19

void setWidth(int width)

20

void setFormatter(HelpFormatter formatter)

21

22

OptionAccessor parse(String[] args)

23

OptionAccessor parse(List<String> args)

24

25

void usage()

26

String usage()

27

28

// Option definition methods

29

def opt(String opt, String description)

30

def opt(String opt, String longOpt, String description)

31

def opt(String opt, String longOpt, String description, boolean required)

32

def opt(String opt, boolean hasArg, String description)

33

def opt(String opt, String longOpt, boolean hasArg, String description)

34

def opt(Map args, String description)

35

}

36

```

37

38

### OptionAccessor

39

40

Interface for accessing parsed command-line options.

41

42

```groovy { .api }

43

interface OptionAccessor {

44

boolean hasOption(String opt)

45

boolean hasOption(char opt)

46

Object getProperty(String property)

47

48

String getOptionValue(String opt)

49

String getOptionValue(char opt)

50

String getOptionValue(String opt, String defaultValue)

51

String[] getOptionValues(String opt)

52

53

List arguments()

54

String[] getArgs()

55

}

56

```

57

58

## Basic Usage Examples

59

60

### Simple CLI Application

61

62

```groovy

63

import groovy.cli.picocli.CliBuilder

64

65

def cli = new CliBuilder(usage: 'myapp [options] <files>')

66

cli.header = 'Process files with various options'

67

cli.footer = 'Example: myapp -v -o output.txt input1.txt input2.txt'

68

69

// Define options

70

cli.h(longOpt: 'help', 'Show usage information')

71

cli.v(longOpt: 'verbose', 'Enable verbose output')

72

cli.o(longOpt: 'output', args: 1, argName: 'file', 'Output file')

73

cli.n(longOpt: 'number', args: 1, argName: 'num', type: Integer, 'Number of iterations')

74

cli.f(longOpt: 'force', 'Force overwrite existing files')

75

76

// Parse arguments

77

def options = cli.parse(args)

78

79

if (!options) {

80

System.exit(1)

81

}

82

83

if (options.h) {

84

cli.usage()

85

System.exit(0)

86

}

87

88

// Access options

89

if (options.v) {

90

println "Verbose mode enabled"

91

}

92

93

def outputFile = options.o ?: 'default-output.txt'

94

def iterations = options.n ?: 1

95

def forceOverwrite = options.f

96

97

println "Output file: $outputFile"

98

println "Iterations: $iterations"

99

println "Force overwrite: $forceOverwrite"

100

101

// Process remaining arguments (files)

102

def files = options.arguments()

103

files.each { file ->

104

println "Processing file: $file"

105

}

106

```

107

108

### Advanced Option Types

109

110

```groovy

111

import groovy.cli.picocli.CliBuilder

112

113

def cli = new CliBuilder()

114

115

// Different option types

116

cli.s(longOpt: 'string', args: 1, 'String option')

117

cli.i(longOpt: 'integer', args: 1, type: Integer, 'Integer option')

118

cli.d(longOpt: 'double', args: 1, type: Double, 'Double option')

119

cli.b(longOpt: 'boolean', type: Boolean, 'Boolean option')

120

cli.f(longOpt: 'file', args: 1, type: File, 'File option')

121

cli.u(longOpt: 'url', args: 1, type: URL, 'URL option')

122

123

// Multiple values

124

cli.m(longOpt: 'multiple', args: '+', 'Multiple string values')

125

cli.l(longOpt: 'list', args: '*', 'Optional multiple values')

126

127

// Required options

128

cli.r(longOpt: 'required', args: 1, required: true, 'Required option')

129

130

def options = cli.parse([

131

'--string', 'hello',

132

'--integer', '42',

133

'--double', '3.14',

134

'--boolean', 'true',

135

'--file', '/path/to/file.txt',

136

'--multiple', 'value1', 'value2', 'value3',

137

'--required', 'must-have'

138

])

139

140

if (options) {

141

println "String: ${options.s}"

142

println "Integer: ${options.i}"

143

println "Double: ${options.d}"

144

println "Boolean: ${options.b}"

145

println "File: ${options.f}"

146

println "Multiple: ${options.ms}" // Returns array

147

println "Required: ${options.r}"

148

}

149

```

150

151

### Validation and Error Handling

152

153

```groovy

154

import groovy.cli.picocli.CliBuilder

155

156

def cli = new CliBuilder(usage: 'validator [options]')

157

158

cli.p(longOpt: 'port', args: 1, type: Integer, 'Port number (1-65535)')

159

cli.e(longOpt: 'email', args: 1, 'Email address')

160

cli.d(longOpt: 'date', args: 1, type: Date, 'Date in format yyyy-MM-dd')

161

162

def options = cli.parse(args)

163

164

if (!options) {

165

println "Failed to parse command line options"

166

System.exit(1)

167

}

168

169

// Custom validation

170

try {

171

if (options.p && (options.p < 1 || options.p > 65535)) {

172

throw new IllegalArgumentException("Port must be between 1 and 65535")

173

}

174

175

if (options.e && !options.e.matches(/[\w._%+-]+@[\w.-]+\.[A-Za-z]{2,}/)) {

176

throw new IllegalArgumentException("Invalid email format")

177

}

178

179

println "All validations passed"

180

181

} catch (Exception e) {

182

println "Validation error: ${e.message}"

183

cli.usage()

184

System.exit(1)

185

}

186

```

187

188

## Advanced Features

189

190

### Subcommands

191

192

```groovy

193

import groovy.cli.picocli.CliBuilder

194

195

class GitLikeApp {

196

static void main(String[] args) {

197

if (args.length == 0) {

198

printUsage()

199

return

200

}

201

202

def command = args[0]

203

def commandArgs = args.length > 1 ? args[1..-1] : []

204

205

switch (command) {

206

case 'init':

207

handleInit(commandArgs)

208

break

209

case 'add':

210

handleAdd(commandArgs)

211

break

212

case 'commit':

213

handleCommit(commandArgs)

214

break

215

default:

216

println "Unknown command: $command"

217

printUsage()

218

}

219

}

220

221

static void handleInit(String[] args) {

222

def cli = new CliBuilder(usage: 'myapp init [options]')

223

cli.b(longOpt: 'bare', 'Create bare repository')

224

cli.q(longOpt: 'quiet', 'Suppress output')

225

226

def options = cli.parse(args)

227

if (!options) return

228

229

println "Initializing repository..."

230

if (options.b) println "Creating bare repository"

231

if (!options.q) println "Repository initialized"

232

}

233

234

static void handleAdd(String[] args) {

235

def cli = new CliBuilder(usage: 'myapp add [options] <files>')

236

cli.A(longOpt: 'all', 'Add all files')

237

cli.v(longOpt: 'verbose', 'Verbose output')

238

239

def options = cli.parse(args)

240

if (!options) return

241

242

if (options.A) {

243

println "Adding all files"

244

} else {

245

options.arguments().each { file ->

246

println "Adding file: $file"

247

}

248

}

249

}

250

251

static void handleCommit(String[] args) {

252

def cli = new CliBuilder(usage: 'myapp commit [options]')

253

cli.m(longOpt: 'message', args: 1, required: true, 'Commit message')

254

cli.a(longOpt: 'all', 'Commit all changes')

255

256

def options = cli.parse(args)

257

if (!options) return

258

259

println "Committing with message: ${options.m}"

260

if (options.a) println "Including all changes"

261

}

262

263

static void printUsage() {

264

println """

265

Usage: myapp <command> [options]

266

267

Commands:

268

init Initialize a new repository

269

add Add files to staging area

270

commit Create a new commit

271

272

Use 'myapp <command> --help' for command-specific options.

273

"""

274

}

275

}

276

```

277

278

### Configuration File Integration

279

280

```groovy

281

import groovy.cli.picocli.CliBuilder

282

import groovy.json.JsonSlurper

283

284

class ConfigurableApp {

285

def config = [:]

286

287

void loadConfig(String configFile) {

288

def file = new File(configFile)

289

if (file.exists()) {

290

def slurper = new JsonSlurper()

291

config = slurper.parse(file)

292

}

293

}

294

295

void run(String[] args) {

296

def cli = new CliBuilder(usage: 'app [options]')

297

cli.c(longOpt: 'config', args: 1, 'Configuration file')

298

cli.h(longOpt: 'host', args: 1, 'Server host')

299

cli.p(longOpt: 'port', args: 1, type: Integer, 'Server port')

300

cli.v(longOpt: 'verbose', 'Verbose output')

301

302

def options = cli.parse(args)

303

if (!options) return

304

305

// Load configuration file if specified

306

if (options.c) {

307

loadConfig(options.c)

308

}

309

310

// Command line options override config file

311

def host = options.h ?: config.host ?: 'localhost'

312

def port = options.p ?: config.port ?: 8080

313

def verbose = options.v ?: config.verbose ?: false

314

315

println "Host: $host"

316

println "Port: $port"

317

println "Verbose: $verbose"

318

319

startServer(host, port, verbose)

320

}

321

322

void startServer(String host, int port, boolean verbose) {

323

if (verbose) {

324

println "Starting server in verbose mode..."

325

}

326

println "Server started on $host:$port"

327

}

328

}

329

```

330

331

### Interactive CLI

332

333

```groovy

334

import groovy.cli.picocli.CliBuilder

335

import java.util.Scanner

336

337

class InteractiveCLI {

338

private Scanner scanner = new Scanner(System.in)

339

private boolean running = true

340

341

void start() {

342

println "Interactive CLI started. Type 'help' for commands."

343

344

while (running) {

345

print "> "

346

def input = scanner.nextLine().trim()

347

if (input) {

348

processCommand(input.split(/\s+/))

349

}

350

}

351

}

352

353

void processCommand(String[] args) {

354

def command = args[0]

355

def commandArgs = args.length > 1 ? args[1..-1] : []

356

357

switch (command) {

358

case 'help':

359

showHelp()

360

break

361

case 'echo':

362

handleEcho(commandArgs)

363

break

364

case 'calc':

365

handleCalculator(commandArgs)

366

break

367

case 'exit':

368

case 'quit':

369

running = false

370

println "Goodbye!"

371

break

372

default:

373

println "Unknown command: $command. Type 'help' for available commands."

374

}

375

}

376

377

void handleEcho(String[] args) {

378

def cli = new CliBuilder(usage: 'echo [options] <text>')

379

cli.n('No newline')

380

cli.u('Uppercase')

381

382

def options = cli.parse(args)

383

if (!options) return

384

385

def text = options.arguments().join(' ')

386

if (options.u) text = text.toUpperCase()

387

388

if (options.n) {

389

print text

390

} else {

391

println text

392

}

393

}

394

395

void handleCalculator(String[] args) {

396

def cli = new CliBuilder(usage: 'calc <operation> <num1> <num2>')

397

398

def options = cli.parse(args)

399

if (!options || options.arguments().size() != 3) {

400

println "Usage: calc <add|sub|mul|div> <number1> <number2>"

401

return

402

}

403

404

try {

405

def operation = options.arguments()[0]

406

def num1 = Double.parseDouble(options.arguments()[1])

407

def num2 = Double.parseDouble(options.arguments()[2])

408

409

def result = switch (operation) {

410

case 'add' -> num1 + num2

411

case 'sub' -> num1 - num2

412

case 'mul' -> num1 * num2

413

case 'div' -> num2 != 0 ? num1 / num2 : { throw new ArithmeticException("Division by zero") }()

414

default -> { println "Unknown operation: $operation"; return }

415

}

416

417

println "Result: $result"

418

419

} catch (NumberFormatException e) {

420

println "Invalid number format"

421

} catch (ArithmeticException e) {

422

println "Math error: ${e.message}"

423

}

424

}

425

426

void showHelp() {

427

println """

428

Available commands:

429

help Show this help message

430

echo [options] <text> Echo text with options

431

-n No newline

432

-u Uppercase

433

calc <op> <n1> <n2> Calculator (add, sub, mul, div)

434

exit, quit Exit the program

435

"""

436

}

437

}

438

439

// Usage

440

new InteractiveCLI().start()

441

```

442

443

## Best Practices

444

445

### Error Handling and User Experience

446

447

```groovy

448

import groovy.cli.picocli.CliBuilder

449

450

class RobustCLI {

451

static void main(String[] args) {

452

try {

453

def app = new RobustCLI()

454

app.run(args)

455

} catch (Exception e) {

456

System.err.println("Unexpected error: ${e.message}")

457

if (System.getProperty('debug')) {

458

e.printStackTrace()

459

}

460

System.exit(1)

461

}

462

}

463

464

void run(String[] args) {

465

def cli = new CliBuilder(usage: 'robustapp [options] <files>')

466

cli.width = 120

467

cli.header = 'A robust CLI application example'

468

cli.footer = '''

469

Examples:

470

robustapp -v file1.txt file2.txt

471

robustapp --output=result.txt --format=json input.csv

472

'''

473

474

// Define comprehensive options

475

cli.h(longOpt: 'help', 'Show this help message')

476

cli.v(longOpt: 'verbose', 'Enable verbose output')

477

cli.q(longOpt: 'quiet', 'Suppress output')

478

cli.o(longOpt: 'output', args: 1, argName: 'file', 'Output file')

479

cli.f(longOpt: 'format', args: 1, argName: 'fmt', 'Output format (json|xml|csv)')

480

cli._(longOpt: 'version', 'Show version information')

481

482

def options = cli.parse(args)

483

484

// Handle parse errors

485

if (!options) {

486

println "Use --help for usage information"

487

System.exit(1)

488

}

489

490

// Handle help and version

491

if (options.h) {

492

cli.usage()

493

return

494

}

495

496

if (options.version) {

497

println "RobustApp version 1.0.0"

498

return

499

}

500

501

// Validate conflicting options

502

if (options.v && options.q) {

503

System.err.println("Error: --verbose and --quiet cannot be used together")

504

System.exit(1)

505

}

506

507

// Validate required arguments

508

if (!options.arguments()) {

509

System.err.println("Error: At least one input file is required")

510

cli.usage()

511

System.exit(1)

512

}

513

514

// Validate format option

515

def validFormats = ['json', 'xml', 'csv']

516

if (options.f && !validFormats.contains(options.f)) {

517

System.err.println("Error: Invalid format '${options.f}'. Valid formats: ${validFormats.join(', ')}")

518

System.exit(1)

519

}

520

521

// Process with validated options

522

processFiles(options)

523

}

524

525

void processFiles(options) {

526

def verbose = options.v && !options.q

527

def outputFile = options.o ?: 'stdout'

528

def format = options.f ?: 'json'

529

530

if (verbose) {

531

println "Processing ${options.arguments().size()} files"

532

println "Output: $outputFile"

533

println "Format: $format"

534

}

535

536

options.arguments().each { file ->

537

if (verbose) println "Processing: $file"

538

// Process file...

539

}

540

541

if (!options.q) {

542

println "Processing completed successfully"

543

}

544

}

545

}

546

```