Hi,
when I have a script that starts with the line
#!/usr/bin/env texlua
I have on my Mac OS X system the full path to my script in arg[0]. Is this portable? That is, if I run this under windows/solaris/... will I always find out where this script is located?
Or other question: is there a more-or-less portable way to find out the location of a script from inside the script? I'd like to set up package.path to a relative directory such as:
package.path = ....
On 11 December 2010 Patrick Gundlach wrote:
Hi,
when I have a script that starts with the line
#!/usr/bin/env texlua
I have on my Mac OS X system the full path to my script in arg[0]. Is this portable? That is, if I run this under windows/solaris/... will I always find out where this script is located?
Hi Patrick, these are two different questions. First, the #! line works everywhere except on Windows. It finds the interpreter (not your script) in PATH. There is no reasonable alternative on Windows. You have to associate filename extensions with interpreters in the registry. If you are using TeX Live, you can start your script from a wrapper instead.
Or other question: is there a more-or-less portable way to find out the location of a script from inside the script? I'd like to set up package.path to a relative directory such as:
package.path = ....
Try this: ------------------------------------------------------- #!/usr/bin/env texlua dirname, filename = string.match(arg[0], '(.*[/\\])(.*)') print('dirname='..dirname) print('filename='..filename) ------------------------------------------------------- it works as expected on Unix: $ testarg0.lua dirname=/usr/local/bin/ filename=testarg0.lua You will certainly get similar results in TeX Live because the location of your script is determined by the wrapper, and hence by kpathsea, which returns the full path. Please note that dirname found by string.match() in the example above contains the leading directory separator already and that backslashes are supported too. In order to install your script under TeX Live, copy it to TEXMFLOCAL/scripts. It must have one of the extensions .tlu, .texlua, or .lua. Then run texhash. For Unix you have to make the script executable and to create symlinks to the script in all bin/<platform> directories. The symlinks should not have extensions. (ln -s relative/path/to/myscript.lua myscript) For Windows, go to bin/win32 and "cp -p runscript.exe myscript.exe" (or, on Windows itself, "copy /b runscript.exe myscript.exe". I'm not sure about Windows in general. I can imagine that it works too if the extension of your script is associated with the texlua binary in the registry. I suppose that this is necessary if you want to run your script under MikTeX. BTW, whether you are using the extension .tlu, .texlua, or .lua doesn't matter if your script is invoked by a wrapper. However, if you associate extensions with interpreters in the registry, I recommend .tlu and .texlua for scripts which depend on texlua. This avoids nasty problems when people install a generic Lua engine too. Regards, Reinhard -- ---------------------------------------------------------------------------- Reinhard Kotucha Phone: +49-511-3373112 Marschnerstr. 25 D-30167 Hannover mailto:reinhard.kotucha@web.de ---------------------------------------------------------------------------- Microsoft isn't the answer. Microsoft is the question, and the answer is NO. ----------------------------------------------------------------------------
Hello Reinhard,
these are two different questions. First, the #! line works everywhere except on Windows. It finds the interpreter (not your script) in PATH. There is no reasonable alternative on Windows. You have to associate filename extensions with interpreters in the registry. If you are using TeX Live, you can start your script from a wrapper instead.
OK, thanks.
Try this: ------------------------------------------------------- #!/usr/bin/env texlua
dirname, filename = string.match(arg[0], '(.*[/\\])(.*)')
print('dirname='..dirname) print('filename='..filename) -------------------------------------------------------
it works as expected on Unix:
On every Unix? I once tried this with sh (echo $0) and if I remember correctly it gave me a relative path on solaris and an absolute path on linux.
You will certainly get similar results in TeX Live
I don't use TeXlive in this case, just one LuaTeX binary for luatex and texlua.
because the location of your script is determined by the wrapper, and hence by kpathsea, which returns the full path. Please note that dirname found by string.match() in the example above contains the leading directory separator already and that backslashes are supported too.
So I assume from your answer that arg[0] in my lua skript is set by kpathsea, and it contains the full path. So I can find out where I am by looking at it? Thanks again Patrick
On 12 December 2010 Patrick Gundlach wrote:
Hello Reinhard,
these are two different questions. First, the #! line works everywhere except on Windows. It finds the interpreter (not your script) in PATH. There is no reasonable alternative on Windows. You have to associate filename extensions with interpreters in the registry. If you are using TeX Live, you can start your script from a wrapper instead.
OK, thanks.
Try this: ------------------------------------------------------- #!/usr/bin/env texlua
dirname, filename = string.match(arg[0], '(.*[/\\])(.*)')
print('dirname='..dirname) print('filename='..filename) -------------------------------------------------------
it works as expected on Unix:
On every Unix? I once tried this with sh (echo $0) and if I remember correctly it gave me a relative path on solaris and an absolute path on linux.
Hi Patrick, it depends on how you invoke the program. If you specify a path on the command line, this path is used. It might be absolute or relative to $PWD. For instance: $ /usr/local/bin/testarg0.lua dirname=/usr/local/bin/ $ ../../../../usr/local/bin/testarg0.lua dirname=../../../../usr/local/bin/ If you don't specify a path, /usr/bin/env searches the binary in PATH. $ testarg0.lua dirname='/usr/local/bin/' I assume that /usr/bin/env is available on all Unix systems. I've read somewhere that some shells require a space after #! but it seems that it's not required on the Unix supported by TeX Live. I'm wondering why you prefer an absolute path. It actually doesn't matter. You can write dirname = string.match(arg[0], '(.*[/\\])(.*)') package.path = dirname .. "../modules/" require "mylib" and mylib.lua will be found regardless whether the path to the program is absolute or relative.
You will certainly get similar results in TeX Live
I don't use TeXlive in this case, just one LuaTeX binary for luatex and texlua.
because the location of your script is determined by the wrapper, and hence by kpathsea, which returns the full path. Please note that dirname found by string.match() in the example above contains the leading directory separator already and that backslashes are supported too.
So I assume from your answer that arg[0] in my lua skript is set by kpathsea, and it contains the full path. So I can find out where I am by looking at it?
Sorry, I was quite unprecise. kpathsea is only involved if the script is launched by a wrapper and the wrappers are only for Windows. They are needed because there are no symlinks on Windows and because we have to set a few environment variables for the hidden Perl and Ghostscript before launching a script. If you want to provide a tiny distribution for several platforms, you can steal some stuff from TeX Live. One nice thing is that, if you use the wrappers, you don't have to put anything into the registry. All you have to do is to add bin/win32 to PATH. The wrappers are explained in detail in bin/win32/runscript.tlu. There is one thing which can cause trouble. Sorry that it didn't come earlier to my mind. Because runscript is mostly written in texlua, it doesn't invoke Lua scripts as usual. For efficiency, it determines the location of the script and then runs the Lua function dofile(). I don't know which impact this has on arg[0], but, if necessary, you can always change runscript.tlu so that it invokes Lua scripts as it invokes Perl scripts, i.e. with os.spawn(). It's advantageous, at least, that the wrappers are written in a scripting language and can easily be adapted. Below is an excerpt from runscript.tlu. Regards, Reinhard Script wrappers in TeX Live on Windows Rationale Wrappers enable use of scripts on Windows as regular programs. They are also required for some binary programs to set up the right environment for them. Batch scripts can be used for wrapping but they are not as universal as binaries (there are some odd cases where they don't work) and it is hard to make them robust and secure. Compiled binary wrappers don't suffer from these problems but they are harder to write, debug and maintain in comparison to scripts. For these reasons a hybrid approach is taken that combines a binary stub with a launcher script. Adding wrappers for user scripts The script wrapping machinery is not limited to scripts shipped with TeX Live. You can also use it for script programs from manually installed packages, which should minimize the problems when using them with TeX Live. First, make sure that there is an interpreter program available on your system for the script you want to use. Interpreters for Perl and Lua are bundled with TeX Live, all others have to be installed independently. Lua scripts are the most efficient to run, so if you consider writing a new script, that would be the recommended choice. The following script types and their file extensions are currently supported and searched in that order: Lua (.tlu;.texlua;.lua) -- included Perl (.pl) -- included Ruby (.rb) -- requires installation Python (.py) -- requires installation Tcl (.tcl) -- requires installation Java (.jar) -- requires installation VBScript (.vbs) -- part of Windows JScript (.js) -- part of Windows Batch (.bat;.cmd) -- part of Windows Finally, Unix-style extensionless scripts are searched as last and the interpreter program is established based on the she-bang (#!) specification on the very first line of the script. This can be an arbitrary program but it must be present on the search path. Next, the script program needs to be installed somewhere below the 'scripts' directory under one of the TEXMF trees (consult the documentation or texmf/web2c/texmf.cnf file for a list). You may need to update the file search database afterwards with: mktexlsr [TEXMFDIR] It is also possible to use scripts that are outside of TEXMF hierarchy by adjusting TEXMFSCRIPTS environment or kpathsea variable, see kpathsea documentation for more information on setting its variables. Test if the script can be located with: kpsewhich --format=texmfscripts <script-name>.<ext> This should output the full path to the script if everything is properly installed and configured. If this test is successful, the script can be run immediately with: runscript <script-name> [script arguments] If you prefer to call the script program simply by its name, copy and rename bin/win32/runscript.exe to <script-name>.exe and put it in bin/win32/ directory of your TeX Live installation or, if you don't have the write permissions there, somewhere else on the search path.]] local docstr = [[ Wrapper structure Wrappers consist of small binary stubs and a common texlua script. The binary stubs are all the same, just different names (but CLI and GUI stubs differ, see below, and GUI stubs are actually all different due to different embedded icons). The job of the binary stub is twofold: (a) call the texlua launcher script 'runscript.tlu' from the same directory (or more precisely from the directory containing 'runscript.dll') and (b) pass to it argv[0] and the unparsed argument string as the last two arguments (after adding a sentinel argument, which ends with a new line character). Arbitrary C strings can be passed, because the script is executed by linking with luatex.dll and calling the lua interpreter internally rather than by spawning a new process. There are two flavours of the binary stub: one for CLI programs and another one for GUI programs. The GUI variant does not open a console window nor does it block the command prompt if started from there. It also uses a dialog box to display an error message in addition to outputting to stderr. The stubs are further split into a common DLL and EXE proxies to it. This is for maintenance reasons - updates can be done by replacement of a single DLL rather than all binary stubs. The launcher script knows, which variant has been used to invoke it based on the sentinel argument. The lack of this argument means that it was invoked in a standard way, i.e. through texlua.exe. All the hard work of locating a script/program to execute happens in the launcher script. The located script/program is always executed directly by spawning its interpreter (or binary) in a new process. The system shell (cmd.exe) is never called (except for batch scripts, of course). If the located script happens to be a (tex)lua script, it is loaded and called internally from within this script, i.e. no new process is spawned. Execution is done using a protected call, so any compile or runtime errors are catched. Source files runscript.tlu launcher script for locating and dispatching target scripts/programs runscript_dll.c common DLL part of the binary stubs; locates and calls the launcher script runscript_exe.c EXE proxy to the common DLL for CLI mode stubs wrunscript_exe.c EXE proxy to the common DLL for GUI mode stubs -- ---------------------------------------------------------------------------- Reinhard Kotucha Phone: +49-511-3373112 Marschnerstr. 25 D-30167 Hannover mailto:reinhard.kotucha@web.de ---------------------------------------------------------------------------- Microsoft isn't the answer. Microsoft is the question, and the answer is NO. ----------------------------------------------------------------------------
participants (2)
-
Patrick Gundlach
-
Reinhard Kotucha