racket/collects/scribblings/guide/scripts.scrbl
Eli Barzilay debd1f9f1e Recketizing much in `net/*', mass ".ss" -> ".rkt" conversion in .scrbl files.
(Some other minor things here and there.)
2011-06-20 04:27:14 -04:00

147 lines
4.6 KiB
Racket

#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.
;}