Since version 22, the emacs executable accepts the --script argument, so the following shebang line can be used for Elisp scripts:

   #!emacs --script

The rest of this article describes the legacy methods.

Legacy method

JohnSwaby presented an idea on the emacs-devel MailingList? on how to use “:;” as an alternative to “#!” to start Emacs scripts.[1]

The shebang (”#!”) works for many languages because their comment character is a number sign, #, and they require only two arguments to run a script.

The sesquicolon (”:;”) works for Emacs because it allows for more than two arguments and it is compatible with the Lisp comment character, the semicolon.

In many versions of Unix, if the first two characters of a script are the shebang, then the program specified after the shebang is run with, optionally, the one argument that can appear on the shebang line. The program is then passed, as its last argument, the script to be interpreted.

If the first two characters of the file are not the shebang; but the first character is a number sign, #, then the C Shell, csh, will be invoked to interpret the script.

If neither of the above, then the Bourne Shell, sh, will be invoked as the interpreter (keeping in mind that on some systems either the Korn Shell, ksh, or the Bourne Again Shell, bash, will stand in for the Bourne Shell).

Emacs requires more than two arguments to run a script; but the sesquicolon allows for this. Since the first two characters of the script are the sesquicolon, :;, the third of the cases described above is the one that applies and, hence, sh (or ksh or bash, as the case may be) is invoked as the interpreter.

The colon is a null command in sh, ksh, or bash, and so does nothing. The semicolon is a command delimiter in all three shells. And, in all three shells, the exec command has the same meaning. Hence,

   :;exec emacs -batch -l "$0" -f main -- "$@"

will have Emacs interpreting the current script, with the entry function being “main”. [ For the Korn shell, replace “$0” with “$_”.]

When Emacs loads the script the colon evaluates to itself (the colon is a self-evaluating constant in current versions of Emacs) and the remainder of the line is ignored as a comment.

The remaining arguments, “$@”, are accessible to your script by way of the variable “command-line-args-left”. The – argument preceding this prevents Emacs from processing those arguments itself (either as arguments to Emacs, or as files to be loaded into buffers). The alternatives are to explicitly set command-line-args-left to nil, or call the function kill-emacs at the exit point of the script.

So, Emacs is already quite fit as an interpreter of scripts. For Emacs use the sesquicolon, for other languages use the shebang. Emacs marches to a different drummer – all the better.

Older versions of Emacs

For older versions of Emacs, the colon does not evaluate. Adding the arguments “-eval ‘(setq : t)’” to the sesquicolon line will take care of Emacs’s evaluation of the colon. MariusVollmer suggested something else, from “Teach Yourself Scheme in Fixnum Days” by Dorai Sitaram:

    ":";exec emacs ...

This works, since “:” is the same as : to the shell and is self-evaluating in all versions of Emacs.

Bash/Sh/Dash/Ksh and CMD/COMMAND scripting ... or Mac and Linux/Unix and Windows/Dos scripting

The following script runs on Linux and Windows. It should also run on Mac X.

    :;exec emacs -batch -l   "$0" -f : "$@" --no-site-file -q  # -*- Emacs-Lisp -*-
    ;     @emacs -batch -l "%~f0" -f :  %*  --no-site-file -q  & goto :EOF
    ; This script will obfuscate a text file, by loading it in, then repeatedly
    ; doing pairs of ^F and ^T commands throughout, and then saving it.  The original
    ; file is kept as a tilde (~) backup.
    ; Usage:  script-name  text-file-name
    ;         To reverse the process and get your original file as a result,
    ;         run the script again.
    ; (No it will not fool any serious cryptographer/programmer, but it will keep folks
    ;  from reading over your shoulder (the results are -really- hard on the eyes) and
    ;  will probably keep your children from ever decoding it.)
    (defun : ()
      (find-file (nth 5 command-line-args))
      (condition-case nil
          (execute-kbd-macro "\^f\^[1\^t" 0)
        (error nil) )
      (save-buffer) )

1) What this means is that the first two lines, especially noting the sesquicolon trick, lets this get handled properly by both CMD and COMMAND, so it should be fine in the Microsoft world, and that it also runs in bash, and sh. dash seems to take it, too. The ‘bash’ part not only lets it run on Linux, Unix, Msys, and Cygwin, but it should also run in the bash on Mac OS X (although without a Mac, I have not been able to verify that.) (And as for Ksh, if $_ is substituted for the $0, it should run there as well.)

2) When it runs in cmd.exe and command.com, the line terminations like LF or CRLF don’t seem to matter. And emacs doesn’t seem to care, either.

3) When this runs in bash/sh, though, at least in the ‘nix world, the first line’s CR (if there is one) needs to be kept away from the tokens there; that is why it is a good idea to leave the number sign there, otherwise bash would make the CR part of one of the arguments and give a “I don’t know what -q(CR) means” kind of error.

4) For Windows or DOS this can be named with either a .cmd or .bat file extension - but .cmd is newer and seems better behaved. The last line is only needed for older systems, since nowadays cmd.exe and command.com assume the :EOF line label at end-of-file if “command extensions” are “enabled”. Really older systems will need to replace the %* in line 2 with

     %1 %2 %3 %4 %5 %6 %7 %8 %9 

(This also runs in Cygwin’s bash, but for unhealthy reasons - - the “exec” in line 1 does run, but doesn’t do anything, so it then runs line 2 just like Dos. Of course the emacs can handle filenames like a:/autoexec.bat , but don’t use something like /cygdrive/c/windows/system32 if your emacs doesn’t know about cygdrives; the NTEmacsWithCygwin page has some help on that.)

(Cygwin is also influenced by the file extension. If the filename ends with either .cmd or .bat, then Cygwin will create a cmd.exe and pass the script to it. If it does not have one of those two extensions, then Cygwin’s bash tries to run it in bash.)

5) This is not intended for Windows Powershell. The syntax there clashes with the :; tokens.

6) This represents how an emacs script really should be written: Figure out that the task to be done is well suited to emacs. Then go into emacs, work it out in lisp or in a macro. Then just put it into a script like the one above. (The one above took about five minutes.)

(Note that a macro can be encapsulated in the “execute-kbd-macro” function above; it can be written with the “insert-kbd-macro” command.) (This one fails, however, when it reaches the end of the buffer; that is why the lisp error catcher “condition-case” is there.)

In batch mode, (princ) writes to stdout. (message) writes to stderr.

I also give credit to Dorai Sitaram and his book about Scheme, for pointing out that prefixing DOS statements, even label statements, with a semicolon still allows it to run, but makes it invisible to Scheme (and eLisp). Hence scripts that run in emacs + windows + unix + Mac OS X.

Here is another one - - if you ever need a random number on the fly, emacs can provide it.

    :;exec emacs -batch -l   "$0" "$@" --no-site-file -q  # -*- Emacs-Lisp -*-
    ;     @emacs -batch -l "%~f0"  %*  --no-site-file -q  & goto :EOF
    ; Gives a (usually) big random integer number, with no arguments.
    ;                  If a positive integer n is given, the range is [0,n) .
      (random t)
      (if (> (length command-line-args-left) 0)
          (princ (random (string-to-number (nth 0 command-line-args-left))))
        (princ (random))       )
      (princ "\n")

(Caveats - A great thing about using emacs is that the random numbers output - if you don’t give a limit - are huge, something like eighteen digits on big machines. But, then, that encourages the user to use similarly big numbers as a limit argument. …. unfortunately, there are upper limits on how big the “positive integer n” limit can be. So if n is outside the plus or minus two gigabyte number, wierd results, like getting negative numbers, might start happening soon. The culprit seems to be the (string-to-number) call, and on my big machine, the positive integer must be in [ -2147483648 , 2147483647 ] or string-to-number returns -2147483648. Too bad, because with 18 digit numbers being spewed, one would like to control numbers with more than ten digits. And concern over the random number generator’s lower bits makes programmers want to use the upper bits. .)