通过 write18 对外部调用命令的定时响应:是否阻塞?

通过 write18 对外部调用命令的定时响应:是否阻塞?

我在想:当我们使用\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 中读取的文件。否则这是不可能的,因为没有用于检查子进程状态的接口(没有,...);在命令执行完成之前,waitLaTeX 可能会完成。\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}

相关内容