我在想:当我们使用\immediate\write18{...}
Latex 在外部 shell 中运行命令时,这是否是\write18
阻塞调用?换句话说,它会等待外部代码完成并返回(成功或失败)吗?或者 Latex 会以非阻塞方式在单独的线程中启动执行,然后立即继续?
能否举一个例子来说明这一点?
答案1
这文档Web2c 解释了基于 Web2c 的 TeX 引擎(例如 TeX Live)的 shell 转义功能。“4.5 Shell 转义”一节:
这shell 命令字符串被传递给命令 shell(通过 C 库函数
system
)。shell 命令不会被转移到任何地方,因此它不会出现在日志文件中,也不会出现在终端输出之外的任何地方。系统调用的退出状态对 TeX 也是不可用的。
该system
函数的解释如下手册页:
system()
命令完成后返回。
因此system
是一个“阻塞调用”。这允许命令创建一个可以\write
在 LaTeX 中读取的文件。否则这是不可能的,因为没有用于检查子进程状态的接口(没有,...);在命令执行完成之前,wait
LaTeX 可能会完成。\end{document}
简而言之:
system
是一个“阻塞调用”\immediate\write18
请注意有和\write18
无 的区别immediate
。后者的执行被推迟到下一页的 shipout。- 请记住安全性。有一些选项可以控制是否可以执行命令。新的限制模式(默认启用)有进一步的限制。
答案2
这是一个自我回答,因为我没有在任何明确的地方找到这个信息 -\immediate\write18
似乎有阻止行为。
我perl
在 Linux 上使用了for
一个sleep
内部带有 的循环作为测试程序;结果发现,\n
对于外部 shell 命令来说,将换行符转义为原始 ASCII 有点麻烦 - 因此,需要小心处理。否则,当使用 调用 Latex 时,下面的 MWE 会产生以下输出-shell-escape
:
$ pdflatex -shell-escape test.tex
This is pdfTeX, Version 3.1415926-2.3-1.40.12 (TeX Live 2011)
\write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2011/06/27>
Babel <v3.8m> and hyphenation patterns for english, dumylang, nohyphenation, lo
aded.
(/path/to/texlive/2011/texmf-dist/tex/latex/base/article.cls
Document Class: article 2007/10/19 v1.4h Standard LaTeX document class
(/path/to/texlive/2011/texmf-dist/tex/latex/base/size10.clo))
(./test.aux)
Check: outputting command:
echo 'for($ix=0;$ix<5;$ix++){ print("perl $ix \n");sleep 1;};' ; perl -e 'for(
$ix=0;$ix<5;$ix++){ print("perl $ix \n");sleep 1;};'
\LaTeX typeout: Before write18
for($ix=0;$ix<5;$ix++){ print("perl $ix
");sleep 1;};
perl 0
perl 1
perl 2
perl 3
perl 4
\LaTeX typeout: After write18
(./test.aux) )
No pages of output.
Transcript written on test.log.
显然,他们\write18
等待perl
代码先执行完毕,然后才允许 Latex 继续运行。
这是 MWE test.tex
:
\documentclass{article}
\begin{document}
% http://tex.stackexchange.com/a/69294/2595
% must escape backslash like this, to have escaped linefeed
% character \n in \write18 output for the shell (raw ASCII)!
\begingroup
\catcode `~=11
\gdef\mytilde{~}
\catcode `\|=0
\catcode `\\=11
|gdef|LF{\n} % \LF becomes (ASCII) "\n" (verbatim char!)
|gdef|n{\n} % \n becomes (ASCII) "\n" (verbatim char!)
|gdef|ELF{\\n} % \ELF becomes (ASCII) "\\n" - escaped backslash for shell!
|endgroup
\edef\cmd{%
%echo 'for($ix=0;$ix<5;$ix++){ print("perl $ix \LF");sleep 1;};'
% below works, with a previous definition of linefeed (with
% non-escaped backslash) \n as macro; then linefeed char is verbatim
%echo 'for($ix=0;$ix<5;$ix++){ print("perl $ix \n");sleep 1;};'
% like this for escaped-backslash-linefeed to propagate through shell;
% the programs see escaped backslash then:
%echo 'for($ix=0;$ix<5;$ix++){ print("perl $ix \ELF");sleep 1;};' ;
%
% The proper one is with \n/\LF; don't forget `;` separators for bash
% (as latex will compact these in a single line anyway)
%
echo 'for($ix=0;$ix<5;$ix++){ print("perl $ix \n");sleep 1;};' ;
perl -e 'for($ix=0;$ix<5;$ix++){ print("perl $ix \n");sleep 1;};'
}
\typeout{%
Check: outputting command: ^^J
\cmd% OK
}
\typeout{^^J \LaTeX typeout: Before write18 ^^J}
\immediate\write18{%
\cmd% OK
}%
\typeout{^^J \LaTeX typeout: After write18 ^^J}
\end{document}