#lang scribble/doc @(require scribble/manual scheme/cmdline "guide-utils.rkt") @title[#:tag "scripts"]{Scripts} Racket files can be turned into executable scripts on Unix and Mac OS X. On Windows, a compatibility layer like Cygwin support the same kind of scripts, or scripts can be implemented as batch files. @section{Unix Scripts} In a Unix environment (including Linux and Mac OS X), a Racket file can be turned into an executable script using the shell's @as-index{@tt{#!}} convention. The first two characters of the file must be @litchar{#!}; the next character must be either a space or @litchar{/}, and the remainder of the first line must be a command to execute the script. For some platforms, the total length of the first line is restricted to 32 characters, and sometimes the space is required. @margin-note{Use @racketmodfont{#lang} @racketmodname[racket/base] instead of @racketmodfont{#lang} @racketmodname[racket] to produce scripts with a faster startup time.} The simplest script format uses an absolute path to a @exec{racket} executable followed by a module declaration. For example, if @exec{racket} is installed in @filepath{/usr/local/bin}, then a file containing the following text acts as a ``hello world'' script: @verbatim[#:indent 2]{ #! /usr/local/bin/racket #lang racket/base "Hello, world!" } In particular, if the above is put into a file @filepath{hello} and the file is made executable (e.g., with @exec{chmod a+x hello}), then typing @exec{./hello} at the shell prompt produces the output @tt{"Hello, world!"}. The above script works because the operating system automatically puts the path to the script as the argument to the program started by the @tt{#!} line, and because @exec{racket} treats a single non-flag argument as a file containing a module to run. Instead of specifying a complete path to the @exec{racket} executable, a popular alternative is to require that @exec{racket} is in the user's command path, and then ``trampoline'' using @exec{/usr/bin/env}: @verbatim[#:indent 2]{ #! /usr/bin/env racket #lang racket/base "Hello, world!" } In either case, command-line arguments to a script are available via @racket[current-command-line-arguments]: @verbatim[#:indent 2]{ #! /usr/bin/env racket #lang racket/base (printf "Given arguments: ~s\n" (current-command-line-arguments)) } If the name of the script is needed, it is available via @racket[(find-system-path 'run-file)], instead of via @racket[(current-command-line-arguments)]. Usually, then best way to handle command-line arguments is to parse them using the @racket[command-line] form provided by @racketmodname[racket]. The @racket[command-line] form extracts command-line arguments from @racket[(current-command-line-arguments)] by default: @verbatim[#:indent 2]{ #! /usr/bin/env racket #lang racket (define verbose? (make-parameter #f)) (define greeting (command-line #:once-each [("-v") "Verbose mode" (verbose? #t)] #:args (str) str)) (printf "~a~a\n" greeting (if (verbose?) " to you, too!" "")) } Try running the above script with the @DFlag{help} flag to see what command-line arguments are allowed by the script. An even more general trampoline uses @exec{/bin/sh} plus some lines that are comments in one language and expressions in the other. This trampoline is more complicated, but it provides more control over command-line arguments to @exec{racket}: @verbatim[#:indent 2]|{ #! /bin/sh #| exec racket -cu "$0" ${1+"$@"} |# #lang racket/base (printf "This script started slowly, because the use of\n") (printf "bytecode files has been disabled via -c.\n") (printf "Given arguments: ~s\n" (current-command-line-arguments)) }| Note that @litchar{#!} starts a line comment in Racket, and @litchar{#|}...@litchar{|#} forms a block comment. Meanwhile, @litchar{#} also starts a shell-script comment, while @exec{exec racket} aborts the shell script to start @exec{racket}. That way, the script file turns out to be valid input to both @exec{/bin/sh} and @exec{racket}. @section{Windows Batch Files} A similar trick can be used to write Racket code in Windows @as-index{@tt{.bat}} batch files: @verbatim[#:indent 2]|{ ; @echo off ; Racket.exe "%~f0" %* ; exit /b #lang racket/base "Hello, world!" }| @;{ Original trick from Ben Goetter, who used: ; @echo off && REM -*- racket -*- ; "%RACKET%" "%~f0" %* ; exit /b #lang racket ... it might be worth documenting the Emacs "-*-" convention and a way to set environment variables -- but that would be needed in the unix part too. ;}