如何将 shell 输出保存到 LaTeX 中的变量?

如何将 shell 输出保存到 LaTeX 中的变量?

我需要能够通过 LaTeX [*] 执行 shell 命令,并将结果输出以 LaTeX“变量”的形式供以后使用(例如,通过后续命令\newcommand)。如果需要,可以使用临时文件来保存输出,但我想避免这种情况。我已经能够使用

\immediate\write18

来调用 bash 命令,但这仅用于写入临时文件。

[*] 我意识到必须设置 -shell-escape 标志才能允许此操作

答案1

TeX 支持文件 IO,你可以利用这一点。要创建一个新的输入文件句柄,你可以执行\newread\readFH;此时,\readFH是一个数字,代表你可以读取或写入的通道(你已经见过其中之一,即特殊通道18)。要打开文件,你可以运行\openin\readFH=filename.ext;现在,从通道读取\readFH将从 中读取行filename.ext。要真正从文件中读取,你可以运行\read\readFH to \nextline;这将从 中读取一行\readFH并将其放入 中\nextline。(有一个警告 - 见下文。)然后使用 来关闭文件\closein\readFH

请注意,这会将换行符视为空格;如果你有一个包含例如

foo
bar

并将一行读入\nextline,则就像您写入 一样\def\nextline{foo }。为了避免这种情况,您可以将 设置\endlinechar-1

总的来说,您的示例将如下所示:

\newread\myinput
% We use '\jobname.temp' to create a uniquely-named temporary file
\immediate\write18{some command > '\jobname.temp'}
\openin\myinput=\jobname.temp
% The group localizes the change to \endlinechar
\bgroup
  \endlinechar=-1
  \read\myinput to \localline
  % Since everything in the group is local, we have to explicitly make the
  % assignment global
  \global\let\myresult\localline
\egroup
\closein\myinput
% Clean up after ourselves
\immediate\write18{rm -f -- '\jobname.temp'}
\dosomething{\myresult}

您可能可以将其抽象为宏,但究竟哪些部分应该参数化可能取决于您的具体用例。

我提到的一个警告:\read操作于平衡的 TeX 文本。这意味着,如果需要匹配括号,它将读取多行,并且它将完成所有这些行。因此,从文件中读取一次

Hello { all
you } people.

将导致字符串Hello { all you } people. 

另外,仅供将来参考:创建要写入的文件句柄是用 完成的\newwrite,使用 打开它们\immediate\openout\writeFH=filename.ext,使用 写入它们\immediate\write\writeFH{some text},使用 关闭它们\immediate\closeout\writeFH。请注意,这some text是实际的 TeX;因此,宏被展开并打印出来例如反斜杠或不匹配的花括号并不简单。(对于反斜杠,有 LaTeX 宏\@backslashchar;请参阅 TeX Stack Exchange 问题“如何制作真正的反斜杠(转义)字符”了解更多一般信息。简短版本是“玩 catcodes”。)

此外,请注意,使用\immediate表示写入命令,而不是读取命令;如果不使用\immediate这些命令,它们将被延迟,直到输出例程运行(即,当 TeX 决定当前页面已完成并且可以发送到 DVI/PDF 时)。根据第 21 章TeXBook, “这种延迟的原因是,它\write通常用于制作索引或目录,而当指令\write出现在段落中间时,特定项目将出现在哪个确切的页面上通常是未知的。”(第 227 页)对于\read和朋友来说,这显然不是一个问题,因此行为有所不同。

答案2

如果您使用 LuaLaTeX,您可以执行以下操作:

\documentclass{article}
\begin{document}
\edef\out{\directlua{
  j = io.popen(" ls -l")
  tex.write(j:read("*all"))
  j:close()
}}
\out
\end{document}

答案3

我不认为你可以让 LaTeX 在排版过程中运行 shell 命令,但我完全错了,事实证明 LaTeX 确实支持这一点。你可以(尽管有这些新信息,我仍然更喜欢)让你的构建系统运行 shell 命令并将其输出存储在文件中,然后使用

\input{filename}

答案4

我对 write18 的特殊机制不太满意(也许我还没有充分探索它)。相反,我将 tex 写为 noweb 文件,它可以包含代码块和文档块。然后我有相对简单的 Makefile(gnu),它以以下方式交互:

  1. 所有代码块均被 notangle 分割出来(放入一个特殊的“代码”目录中),并且它们的执行位被 chmod 。

  2. 使用 Makefile 中的不同目标,所需的程序会被编译(如果是 C)或者如果是 shell 或 awk 则仅运行...并产生 tex 输出。

  3. 然后,此 tex 输出、表格、计算、图形等都会包含回到 latex 文档中。

  4. pdflatex 运行了几次(在 noweave 文档上)来解析前向引用和其他内容。

最终的结果是,当我处理某些数据集、生成统计计算和图片时,我只需说“make”,然后我就能获得所有数据、所有计算和所有图片(必要时还会刷新),有时会在几个小时后(!)。

这种方法还不够完美,我不知道从 latex 文件到 Makefile 以及返回数据在文件系统中的位置等信息的好方法,某些数据必须重复,这很容易出错,但我很高兴我可以把处理我数据的所有代码塞进文档中,详细解释必要的部分,参考我描述问题出现原因的文档(单独章节),以及运行的所有参数,然后只需输入 make。希望这能有所帮助。

相关内容