Building a Robust Crystal CLI with OptionParser: A Hands‑On Guide
Learn how to build a type‑safe CLI in Crystal using OptionParser. This guide covers flag parsing, positional arguments, error handling, and verification steps to ensure a reliable command‑line tool.
07 Sept 2025, 20:27 UTC

Desired Outcome
By the end of this guide you will have a compiled Crystal binary that accepts typed flags, required positional arguments, and automatically displays a helpful usage message. You will also know how to validate input, handle errors, and confirm that your CLI behaves as expected.
Prerequisites
- Crystal 1.9 or newer installed (check with
crystal --version). - A basic understanding of Crystal syntax and static typing.
- Access to a terminal and permission to compile binaries.
Step‑by‑Step Procedure
Create the Project Skeleton
Run
crystal init app my_clito generate a new project. Navigate intomy_cliand opensrc/my_cli.crfor editing.Define the OptionParser DSL
At the top of
src/my_cli.cr, import the standard library:require "option_parser"Now declare a parser instance and set a banner that will be shown with
-h/--help:parser = OptionParser.new do banner "Usage: my_cli [options] " # Flag that expects an integer value flag "-n", "--number NUM", "Number of times to repeat" do |num| # Convert the string to an Int32; raise if conversion fails $repeat_count = num.to_i end # Optional boolean switch switch "-v", "--verbose", "Enable verbose output" # Show help when requested on "-h", "--help", "Show this help" do puts parser exit end endNotice the use of a global variable (prefixed with
$) to hold parsed values; this keeps the parser block concise.Parse the Arguments
Invoke
parse!to processARGV. This method removes any flags it consumes, leaving only positional arguments:begin parser.parse! rescue e : OptionParser::ParseError puts e.message puts parser exit 1 endHandle Positional Arguments
After parsing,
ARGVshould contain the remaining items. For this example we expect exactly one positional argument: an input file path.if ARGV.size != 1 puts "Error: Exactly one input file is required." puts parser exit 1 end input_file = ARGV[0]Implement Core Logic
With the parsed options in place, write the main functionality. Here we simply print the file name and repeat a message the specified number of times.
puts "Processing file: #{input_file}" (count = $repeat_count || 1).times do |i| if $verbose puts "[#{i+1}/#{count}] Verbose: processing #{input_file}" else puts "Processing #{input_file}" end end
Expected Checks and Verification
- Help Text: Run
./my_cli --helpand confirm the banner and flag descriptions appear. - Type Validation: Pass a non‑numeric string to
--number(e.g.,--number abc) and ensure the program exits with a parse error message. - Positional Argument Integrity: Execute
./my_cli file.txtand verify thatARGVis empty after parsing, meaning no stray arguments remain. - Verbose Switch: Run
./my_cli -v file.txtand check that the output includes the verbose prefix.
Recovery and Error Handling
OptionParser itself does not enforce required flags. If your tool needs a mandatory option, add a manual check after parse!:
if $repeat_count.nil?
puts "Error: --number is required."
puts parser
exit 1
end
Because parse! mutates ARGV, be careful to perform all flag parsing before accessing positional arguments. Reordering can cause index errors.
Limitations of OptionParser
- It does not support sub‑commands out of the box; you would need to parse the first positional argument as a command and then re‑invoke a new parser.
- Custom type validation beyond simple casts requires explicit code.
- When a flag is defined with a block, the block runs immediately upon encountering the flag; this can complicate ordering if you need to perform actions after all flags are parsed.
Practical Checklist Before Release
- Compile with
crystal build --release src/my_cli.cr. - Test the binary on all target platforms (Linux, macOS, Windows).
- Run
./my_cli --helpand verify formatting. - Execute unit tests that simulate various flag combinations and validate exit codes.
- Package the binary with a small README that documents usage.
Conclusion
Crystal’s OptionParser offers a concise, type‑safe way to build command‑line tools. By combining its DSL with static typing, explicit error handling, and post‑parse validation, you can deliver a predictable and user‑friendly interface. Remember to keep flag parsing first, perform manual checks for required options, and test the resulting binary across environments.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.