# Using shell escape to access system time

In an earlier question, I asked for analogs of \pdfelapsedtime (which measures the time since the start of the current run) in XeTeX and LuaTeX. A LuaTeX solution was provided, and is now part of Heiko's pdftexcmds package. However, no XeTeX solution was provided. The only approach I can see to measuring time in XeTeX is to use shell-escape.

With pdfTeX on a Unix system I would build a wrapper around

\everyeof{\noexpand}
\catcode\%=12
\message{\input|"date +%s.%N"}


(compile with pdftex --shell-escape).

1. Is there an analog of the \input|... construction for XeTeX? (When writing this question I thought that this construction would work.)

2. How can the system time be accessed in various os?

-
Does the | syntax work even on Windows when using MiKTeX? I have a feeling its TeX Live-specific. – Joseph Wright Jul 30 '12 at 5:54
@Joseph: Theoretically you can use the | syntax on miktex if you use the option --enable-pipes. --shell-escape is not needed. But the behaviour is inconsistent throught the engines: \input "|date /t" works fine with pdflatex, gives with lualatex an error, and no error but also no output with xelatex. – Ulrike Fischer Jul 30 '12 at 7:37
Could this answer help? – egreg Jul 30 '12 at 9:46
@UlrikeFischer Thanks for that: I knew I'd seen something on this topic. – Joseph Wright Jul 30 '12 at 12:07

Pipe support

pdfTeX support pipes since version 1.40.0 (released 2007-01-01). From NEWS:

shell escape: if the first character of a filename for \openin, \openout \input is a pipe symbol (|), the filename is assumed to be a request for a pipe to a command line that is given in the rest of the filename

The feature is enabled in different ways depending on the TeX distribution:

• TeX Live: --shell-restricted (default) allows some few commands to be called (e.g. kpsewhich, makeindex. See variable shell_escape_commands in texmf.cnf. Otherwise full shell escape rights are needed. They can be set by option --shell-escape'.
• MiKTeX: Pipe support is activated by --enable-pipes'. Shell escape options do not affect pipes.

Package catchfile (for plain and LaTeX formats) will add \CatchPipeDef and \CatchPipeEdef in version 2012/07/30 v1.6 to support pdfTeX's pipe feature.

LuaTeX does not have the same pipe feature of pdfTeX, because it has the Lua function io.popen that can be used for that purpose. Pacakge pdftexcmds provides \pdf@pipe as wrapper for convenience.

XeTeX: I could not get pipes running and working neither in TL2011/Linux nor MiKTeX 2.9. In the latter case I could only catch an empty line instead of the intended program output.

OS independent command line

Nowadays TeX distributions often include LuaTeX with its program texlua. For example, the following Lua script prints the time in seconds since 1970-01-01:

if os.gettimeofday then
print(os.gettimeofday())
else
print(os.date("%s"))
end


Then the command call

texlua gettimeofday.lua


prints 1343650327.0485, for example.

However if gettimeofday.lua is not in the current directory, texlua will not find it, because its kpathsea module is not active. At least TL and MiKTeX provide kpsewhich for finding the file.

Example for pdfTeX and the new version (2012/07/30 v1.6) of package catchfile:

\CatchPipeEdef\ScriptGettimeofday{kpsewhich gettimeofday.lua}{\endlinechar=-1}
\CatchPipeEef\CurrentTime{texlua \ScriptGettimeofday}{\endlinechar=-1}


Macro \CurrentTime contains the result. Or with low level commands (with e-TeX), it is something like:

\begingroup
\everyeof{\noexpand}%
\endlinechar=-1 %
\edef\file{\input |"kpsewhich gettimeofday.lua"}
\xdef\CurrentTime{\input |"texlua \file"}
% Use \@@input in LaTeX instead of the redefined \input
\endgroup


PS: The new version of package catchfile: catchfile-1.6.pdf
(The .dtx file is attached to the PDF. Run tex on catchfile.dtx to unpack the package.)

If pipes are not available, then the ordinary write18 feature can be used. However there are many downsides:

• Slower (file writing, searching and reading)
• The wrong file might be found. That can be tested by writing some random bytes in a file with the write18 process and reading them back and checking, whether bytes are the same.
• The solution cannot be made expandable.
• More things that can break: File is not written (e.g. readonly directory), cannot be found, the wrong file is found, …
• Unique temporary file names needed to avoid conflicts in multi-user systems or multiple processes at the same time.
• Cleanup: Delete commands are again OS dependent (rm in Unix, del in Windows). or the temporary files remain.
-
Even without a pipe, it should be possible to use write18 to call texlua, write the result to a file, and input that file, no? The pipe only makes the process easier, and allows for expandability. Shell-escape should still let us define a \settoelapsedtime<count register> in XeTeX. – Bruno Le Floch Jul 30 '12 at 19:41
Actually I'm wondering about some of the limitations you describe. Assume that I write a \jobname.lua file, then do texlua \jobname.lua, creating \jobname.timestamp (cf. my other answer). Am I too naive to think that: (1) The correct file has to be found, since it is in the same directory. (2) The directory cannot be readonly, otherwise the log, aux, pdf files could not be written there. (3) Multi-user systems compiling the same \jobname in the same directory will break anyways with the log, pdf, etc. (4) Deleting can be done from within the lua script: os.remove("\jobname.lua"). – Bruno Le Floch Jul 31 '12 at 10:43
@BrunoLeFloch It is possible that the current directory is read-only. Therefore option --output-directory exists that redirects writes (\openout, \write) to the specified writable directory. However, at TeX macro level or inside shell escape commands this is unknown. – Heiko Oberdiek Jul 31 '12 at 12:05
Thanks, @HeikoOberdiek. In the case you describe, LaTeX's aux file mechanism will fail, for instance, and many other packages which require extra files too. – Bruno Le Floch Jul 31 '12 at 15:51
@HeikoOberdiek it seems you did not release to CTAN that version, is there any reason (security perhaps) ? – jfbu Feb 16 at 13:45

One approach, based on Heiko Oberdiek's answer to this question, and egreg's answer to a similar question is the code below. It's made more complicated because I don't know how to pass parameters to a lua script interpreted using texlua, so I hard-code the parameter \blf@time@base in the lua file each time a function is called.

Since the absence of pipes in XeTeX forbids any expandable solution, the function \pdf@setelapsedtime should be called before each time \pdf@elapsedtime is used. This solution using \write18 has all the drawbacks noted in Heiko's answer. In particular, each \test takes 0.1 seconds on my system, which is rather slow.

\catcode\@=11
\newwrite\blf@time@write
\gdef\blf@time@base{0}%
\protected\gdef\blf@time@aux#1#2%
{%
\begingroup
\escapechar=-1
\immediate\openout\blf@time@write\jobname.lua%
\immediate\write\blf@time@write
{%
os.remove("\jobname.timestamp")
if os.gettimeofday then
time = os.gettimeofday()
else
time = os.date("\string\%s")
end
val = .5 + 65536 * (time - \blf@time@base)
if val > 2147483647 then
val = 2147483647
end
io.output("\jobname.timestamp"):write(string.format("%
\string\%f\string\\n\string\%d", time, val))
}%
\immediate\closeout\blf@time@write
\immediate\write18{texlua \jobname.lua}%
\endlinechar-1%
#1%
#2%
\endgroup
}%
\protected\gdef\pdf@resettimer
{\blf@time@aux{\xdef\blf@time@base{\blf@time@tmp}}{}}%
\protected\gdef\pdf@setelapsedtime
{%
\blf@time@aux{}%
{\protected\xdef\pdf@elapsedtime{\numexpr\blf@time@tmp\relax}}%
}%
\pdf@resettimer
\pdf@setelapsedtime

\def\test
{%
\pdf@setelapsedtime
\message{\the\pdf@elapsedtime}%
}
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\test\test\test\test\test\test\test\test\test\test
\bye


Compile with xetex --shell-escape <filename>.tex.

-