lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


My recently released runlua utility has an option to load and skip over any
initial shell code in the file. But I just discovered a neat trick which
obviates the need for such a feature.

  #!/bin/sh
  _=[[
  exec lua "$0" "$@"
  ]]
  print("Running", _VERSION)
  print("Options", ...)

Probably others have figured this out, too, but it's new to me. It's an
admixture of Lua and shell code. Lua will safely parse the whole thing
because the shell code is quoted as a long string. The shell will also
safely execute the file: 1) _=[[ is a valid shell assignment, and 2) shells
effectively tokenize and evaluate files line-by-line, command-by-command and
won't attempt to parse past the exec command.

The problem with shebang usage is that the interpreter must be a
fully-qualified path. POSIX doesn't guarantee any particular location, but
in practice /bin/sh and /usr/bin/env usually exist.

The standard trick for running Lua or any other interpreter is to do

  #!/usr/bin/env lua

because env will search for a command named lua in all ${PATH} directories.

However, unlike Perl or Python, the command "lua" might not even exist. For
example, on OpenBSD there's no "lua" command even though I've installed both
Lua 5.1 and Lua 5.2 ports packages. Instead there's only lua51 and lua52.
Contrast that with Ubuntu Linux which usually name them lua5.1 and lua5.2,
and has lua as a symlink to one or the other.

While my runlua utility I published earlier has additional features,
including the ability to pick a particular version, the following is an easy
and portable way for a simple Lua script to use shebang and actually work
out-of-the-box on systems other than the developer's.

  #!/bin/sh
  _=[[
  IFS=:
  for D in ${PATH}; do
    for F in "${D}"/lua "${D}"/lua5* "${D}"/luajit*; do
      if [ -x "${F}" ]; then
        exec "${F}" "$0" "$@"
      fi
    done
  done
  printf "%s: no Lua interpreter found\n" "${0##*/}" >&2
  exit 1
  ]]

  print("Running", _VERSION)
  print("Options", ...)