Search code examples
command-line-interfaceracketcommand-line-tool

Confused about how to use command-line form: unbound identifier in module and extracting arguments


I'm trying to make a basic (but specialized) command-line file search tool in Racket for my own personal use. Here is what I have so far:

#! /usr/bin/env racket
#lang racket/base

(require racket/path)
(require racket/cmdline)
(require racket/string)

(define (basename f)
  (path->string (file-name-from-path f)))

(define (plain-name? path)
  (regexp-match? "^[A-Za-z]+$" (basename path)))

(define (find-csv root [name ""] [loc ""] [owner ""] [max 10000])
  (define m 0)
  (for ([f (in-directory root plain-name?)]
        #:when (and (file-exists? f)
                    (filename-extension f)
                    (bytes=? #"csv" (filename-extension f))
                    (string-contains? (path->string f) loc)
                    (string-contains? (basename f) name))
        #:break (>= m max))
    (set! m (add1 m))
    (displayln f)))

(define args
  (command-line
   #:once-each
   [("-n" "--max-paths") "Maximum number of file paths to return" (max 10000)]
   [("-o" "--owner") "Limit search to certain owners" (owner "")]
   [("-t" "--template") "Limit search to certain templates" (template "")]
   [("-l" "--location")
    "Limit search to directory structure containing location"
    (loc "")]
   [("-p" "--root-path") "Look under the root path" (root "/home/wdkrnls")]))

My first issue is that I'm getting an error when trying to run it via

./list-files.rkt

list-files.rkt:30:55: owner: unbound identifier in module
  in: owner
  context...:
   standard-module-name-resolver

This use of command-line looks like it follows the greeting example in the Racket documentation, but racket seems to be looking for defined functions where I'm just trying to specify default values to variables I'd like to make available to my find-csv function.

My second issue is it's not clear to me from the documentation how I am supposed to use these arguments to call find-csv from the command line. The example in the documentation only covers a very basic case of one argument. I have no required arguments, but several optional ones.

Can you give me any pointers? Thanks.


Solution

  • In the example in the documentation, all of the flags toggle parameters that are used to communicate the flag settings to the later processing step (which isn't shown in the example).

    So in your particular case, you would define parameters like this:

    (define max (make-parameter 10000))
    (define owner (make-parameter ""))
    (define template (make-parameter "")
    (define loc (make-parameter ""))
    (define root (make-parameter "/home/wdkrnls"))
    

    where the arguments here are the parameter default values. Then you can either call find-csv with the parameter value (by using the parameter as a function, e.g., like (max)), or you can change find-csv so that it doesn't take any arguments and instead just accesses the parameters directly.

    Also, your flag syntax is probably not quite right. You probably want something like this for each one:

    [("-n" "--max-paths") max-arg
                          "Maximum number of file paths to return"
                          (max max-arg)]
    

    Note the max-arg name that's added. You need that to actually set the parameter to the value provided by the user.