1 Shell Commands
| (require recspecs/shell) | package: recspecs-lib | 
The recspecs/shell module provides tools for testing interactive shell commands, inspired by the Unix expect tool. It supports both simple transcript-based testing and advanced pattern-based automation.
1.1 Basic Shell Testing
(require recspecs/shell) (expect/shell "cat" "> hi""\n" "hi""\n" "> there""\n" "there") 
1.2 Pattern-Based Shell Automation
For complex interactive scenarios, expect/shell/patterns provides a declarative pattern-matching approach similar to the Unix expect tool.
- #:timeout — Sets the default timeout in seconds for the entire session (default: 30) 
- #:strict? — Controls whether whitespace normalization is applied (default: #f) 
- string-expr or (exact string-expr) — Exact string matching 
- (regex regex-expr) — Regular expression matching with capture group support 
- (glob glob-string-expr) — Glob pattern matching with * and ? wildcards 
- (timeout seconds-expr) — Matches when the specified timeout is reached 
- eof — Matches when the process terminates 
- (send-input text-expr) — Send text as input to the process 
- continue — Proceed to the next pattern 
- retry — Retry the current pattern 
- (error message-expr) — Raise an error with the given message 
- procedure-expr — Call a custom procedure with session and variables 
Examples:
(expect/shell/patterns "bash" ["$" (send-input "echo hello")] ["hello" (send-input "exit")]) 
(expect/shell/patterns "slow-server" #:timeout 60 [(regex #rx"Server started on port ([0-9]+)") (send-input "connect")] [(timeout 30) (error "Server startup timeout")] ["Connected" continue]) 
(expect/shell/patterns "deployment-script" [(glob "*$ ") (send-input "deploy app")] [(glob "*Success*") continue] [(glob "*Error*") (error "Deployment failed")] [(glob "*Warning*") continue]) 
(expect/shell/patterns "bash" ["$" (send-input "echo 'port: 8080'")] [(regex #rx"port: ([0-9]+)") (send-input "connect $0")] ["connected" (send-input "exit")]) 
1.3 Advanced Pattern Features
1.3.1 Variable Capture
When using (regex regex-expr) patterns, capture groups are automatically extracted and made available for variable substitution in subsequent actions. Variables are referenced as $0, $1, $2, etc., where $0 is the first capture group.
(expect/shell/patterns "date" [(regex #rx"([A-Z][a-z]+) ([0-9]+)") (send-input "echo Month: $0, Day: $1")]) 
1.3.2 Flow Control
- continue — Advances to the next pattern in the sequence 
- retry — Repeats the current pattern (useful for polling) 
- (error msg) — Terminates with a controlled error 
(expect/shell/patterns "build-system" ["Building..." continue] [(regex #rx"Progress: ([0-9]+)%") retry] ["Build complete" continue] [(glob "*failed*") (error "Build failed")]) 
1.3.3 Timeout Handling
Individual patterns can specify timeouts, and a global session timeout can be set:
(expect/shell/patterns "long-running-process" #:timeout 300 ["Starting..." continue] [(timeout 60) (send-input "status")] ["Progress: 100%" continue] [(timeout 300) (error "Process timeout")]) 
1.3.4 Custom Actions
For complex logic, actions can be procedures that receive the session state and captured variables:
(define (analyze-output session vars) (if (> (length vars) 0) (printf "Captured: ~a~n" (car vars)) (printf "No captures~n")) 'continue) (expect/shell/patterns "analyzer" [(regex #rx"Result: (.+)") analyze-output]) 
1.4 Pattern Matching Reference
procedure
(match-pattern pattern text vars) → 
boolean? list? string? pattern : pattern? text : string? vars : list? 
This function underlies the pattern matching in expect/shell/patterns and can be used directly for testing pattern logic.
(define-values (matched? vars text) (match-pattern (pattern-regex #rx"port: ([0-9]+)") "port: 8080" '())) 
1.5 Error Handling and Debugging
When patterns fail to match or timeouts occur, expect/shell/patterns provides detailed error messages including:
- The accumulated output at the time of failure 
- The pattern that was being matched 
- Suggestions for common issues 
For debugging complex interactions, enable verbose mode with recspecs-verbose? or the RECSPECS_VERBOSE environment variable to see real-time output.
1.6 Migration from Basic Shell Testing
Existing expect/shell tests can be gradually migrated to the pattern-based approach for enhanced functionality:
(expect/shell "interactive-app" "> start""\n" "Ready""\n" "> process data.txt""\n" "Processing...""\n" "Done""\n" "> quit") (expect/shell/patterns "interactive-app" ["$" (send-input "start")] ["Ready" (send-input "process data.txt")] [(glob "*Processing*") continue] ["Done" (send-input "quit")]) 
1.7 Pattern and Action Structures
The pattern-based shell automation is built on the following structures, which can be used directly for advanced scenarios:
struct
(struct pattern-action (pattern action vars) #:extra-constructor-name make-pattern-action) pattern : pattern? action : action? vars : list? 
struct
(struct pattern-exact (text) #:extra-constructor-name make-pattern-exact) text : string? 
struct
(struct pattern-regex (regex) #:extra-constructor-name make-pattern-regex) regex : regexp? 
struct
(struct pattern-glob (pattern) #:extra-constructor-name make-pattern-glob) pattern : string? 
struct
(struct pattern-timeout (seconds) #:extra-constructor-name make-pattern-timeout) seconds : number? 
struct
(struct pattern-eof () #:extra-constructor-name make-pattern-eof) 
struct
(struct action-send-text (text) #:extra-constructor-name make-action-send-text) text : string? 
struct
(struct action-continue () #:extra-constructor-name make-action-continue) 
struct
(struct action-retry () #:extra-constructor-name make-action-retry) 
struct
(struct action-error (message) #:extra-constructor-name make-action-error) message : string? 
struct
(struct action-proc (proc) #:extra-constructor-name make-action-proc) proc : procedure? 
procedure
(shell-run-patterns cmd patterns [ #:timeout timeout]) → void? cmd : (or/c string? (listof string?)) patterns : (listof pattern-action?) timeout : number? = 30 
(shell-run-patterns "bash" (list (pattern-action (pattern-exact "$") (action-send-text "echo test") '()) (pattern-action (pattern-exact "test") (action-send-text "exit") '()))) 
1.8 Best Practices
- Use specific patterns to avoid false matches: prefer (exact "$ ") over "$" 
- Include timeout patterns for long-running operations 
- Use continue judiciously to handle intermediate output 
- Capture important values with regex patterns for reuse 
- Test pattern logic in isolation using match-pattern 
- Enable verbose mode during development for better visibility 
- Consider the order of patterns carefully: more specific patterns should come before general ones 
- Use eof patterns to handle unexpected process termination gracefully