← スキル一覧に戻る
Command-Line Parsing with

building-racket-clis
by Diogenesoftoronto
chaotic rebirth, from the chrysalis of knowledge
⭐ 4🍴 0📅 2026年1月20日
SKILL.md
name: building-racket-clis description: Builds Racket command-line applications with proper argument parsing, subcommands, and packaging. Use when creating CLI tools, adding command-line options, or packaging Racket executables.
Building Racket CLI Applications
Quick Start
#lang racket
(require racket/cmdline)
(define verbose (make-parameter #f))
(command-line
#:program "my-cli"
#:once-each
[("-v" "--verbose") "Enable verbose output" (verbose #t)]
#:args (filename)
(when (verbose) (displayln "Processing..."))
(displayln filename))
Command-Line Parsing with racket/cmdline
Basic Flags and Arguments
(require racket/cmdline)
(define output-file (make-parameter "out.txt"))
(define count (make-parameter 1))
(command-line
#:program "tool"
#:once-each
[("-o" "--output") file "Output file path" (output-file file)]
[("-n" "--count") n "Number of iterations" (count (string->number n))]
#:once-any
[("--json") "Output as JSON" (format-param 'json)]
[("--csv") "Output as CSV" (format-param 'csv)]
#:multi
[("-i" "--include") path "Include additional path" (includes (cons path (includes)))]
#:args (input-file . rest-files)
(process-files (cons input-file rest-files)))
Flag Types
| Directive | Purpose |
|---|---|
#:once-each | Flag can appear once |
#:once-any | Only one of these flags allowed |
#:multi | Flag can repeat, accumulates values |
#:final | Stops processing after this flag |
#:args | Positional arguments pattern |
#:usage-help | Custom usage message |
Subcommands Pattern
#lang racket
(require racket/cmdline)
(define (cmd-init args)
(command-line #:program "mycli init"
#:argv args
#:args () (displayln "Initialized!")))
(define (cmd-run args)
(define watch (make-parameter #f))
(command-line #:program "mycli run"
#:argv args
#:once-each [("-w" "--watch") "Watch mode" (watch #t)]
#:args (file) (run-file file (watch))))
(define (main)
(define args (current-command-line-arguments))
(when (zero? (vector-length args))
(displayln "Usage: mycli <command> [options]")
(displayln "Commands: init, run")
(exit 1))
(match (vector-ref args 0)
["init" (cmd-init (vector-drop args 1))]
["run" (cmd-run (vector-drop args 1))]
[cmd (eprintf "Unknown command: ~a~n" cmd) (exit 1)]))
(module+ main (main))
Packaging as Executable
Method 1: raco exe (Standalone Binary)
# Create standalone executable
raco exe -o my-cli main.rkt
# Create distribution with dependencies
raco distribute dist-folder my-cli
Method 2: Launcher via info.rkt (Installed with Package)
In info.rkt:
#lang info
(define collection "my-package")
(define deps '("base"))
;; Define CLI launchers
(define racket-launcher-names '("my-cli" "my-cli-admin"))
(define racket-launcher-libraries '("main.rkt" "admin.rkt"))
Install with:
raco pkg install --link .
# Now 'my-cli' is available in PATH
Method 3: GraalVM Native Image (Advanced)
# Compile to bytecode
raco make main.rkt
# Use racket-native or wrap in GraalVM (experimental)
Interactive Input
;; Simple prompt
(define (prompt msg)
(display msg)
(flush-output)
(read-line))
;; Password input (no echo - requires terminal)
(define (prompt-password msg)
(display msg)
(flush-output)
(system "stty -echo")
(define pw (read-line))
(system "stty echo")
(newline)
pw)
;; Confirmation
(define (confirm? msg)
(define response (prompt (format "~a [y/N]: " msg)))
(member (string-downcase (string-trim response)) '("y" "yes")))
Output Formatting
Colored Output
(require racket/format)
(define (color code text)
(format "\033[~am~a\033[0m" code text))
(define (red text) (color 31 text))
(define (green text) (color 32 text))
(define (yellow text) (color 33 text))
(define (blue text) (color 34 text))
(define (bold text) (color 1 text))
(displayln (green "✓ Success"))
(displayln (red "✗ Error"))
Progress Indicators
(define (with-spinner msg thunk)
(define frames '("⠋" "⠙" "⠹" "⠸" "⠼" "⠴" "⠦" "⠧" "⠇" "⠏"))
(define done? (box #f))
(define spinner-thread
(thread
(λ ()
(let loop ([i 0])
(unless (unbox done?)
(printf "\r~a ~a" (list-ref frames (modulo i 10)) msg)
(flush-output)
(sleep 0.1)
(loop (add1 i)))))))
(define result (thunk))
(set-box! done? #t)
(thread-wait spinner-thread)
(printf "\r✓ ~a~n" msg)
result)
Exit Codes
;; Standard exit codes
(define EXIT-SUCCESS 0)
(define EXIT-ERROR 1)
(define EXIT-USAGE 64) ; EX_USAGE from sysexits.h
(define EXIT-DATAERR 65) ; EX_DATAERR
(define EXIT-NOINPUT 66) ; EX_NOINPUT
(define (die! msg [code EXIT-ERROR])
(eprintf "Error: ~a~n" msg)
(exit code))
;; Usage
(unless (file-exists? input-file)
(die! (format "File not found: ~a" input-file) EXIT-NOINPUT))
Environment Variables
;; Reading
(define api-key (getenv "API_KEY"))
(define debug? (equal? (getenv "DEBUG") "1"))
(define home (or (getenv "HOME") (find-system-path 'home-dir)))
;; With defaults
(define port (string->number (or (getenv "PORT") "8080")))
;; Setting (for child processes)
(putenv "MY_VAR" "value")
Configuration Files
XDG-Compliant Config Location
(define (config-dir)
(or (getenv "XDG_CONFIG_HOME")
(build-path (find-system-path 'home-dir) ".config")))
(define (app-config-path app-name)
(build-path (config-dir) app-name "config.toml"))
Simple Config Loading
(require json)
(define (load-config path)
(if (file-exists? path)
(with-input-from-file path read-json)
(hash)))
(define (save-config path data)
(make-parent-directory* path)
(with-output-to-file path
#:exists 'replace
(λ () (write-json data))))
Testing CLIs
#lang racket
(require rackunit)
;; Capture stdout/stderr
(define (capture-output thunk)
(define out (open-output-string))
(define err (open-output-string))
(parameterize ([current-output-port out]
[current-error-port err])
(thunk))
(values (get-output-string out)
(get-output-string err)))
;; Test CLI with args
(define (run-cli-test args)
(parameterize ([current-command-line-arguments (list->vector args)])
(capture-output main)))
(test-case "help flag shows usage"
(define-values (out err) (run-cli-test '("--help")))
(check-regexp-match #rx"Usage:" out))
Best Practices
- Use parameters for configuration, not global variables
- Validate inputs early with clear error messages
- Support
--helpautomatically via command-line - Use exit codes consistently (0 = success)
- Write to stderr for errors and diagnostics
- Support
--versionfor installed tools - Handle signals gracefully when possible
;; Version flag
(command-line
#:program "my-cli"
#:once-each
[("--version") "Show version"
(displayln "my-cli v1.0.0") (exit 0)]
...)
スコア
総合スコア
60/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です