我需要能够通过 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),它以以下方式交互:
所有代码块均被 notangle 分割出来(放入一个特殊的“代码”目录中),并且它们的执行位被 chmod 。
使用 Makefile 中的不同目标,所需的程序会被编译(如果是 C)或者如果是 shell 或 awk 则仅运行...并产生 tex 输出。
然后,此 tex 输出、表格、计算、图形等都会包含回到 latex 文档中。
pdflatex 运行了几次(在 noweave 文档上)来解析前向引用和其他内容。
最终的结果是,当我处理某些数据集、生成统计计算和图片时,我只需说“make”,然后我就能获得所有数据、所有计算和所有图片(必要时还会刷新),有时会在几个小时后(!)。
这种方法还不够完美,我不知道从 latex 文件到 Makefile 以及返回数据在文件系统中的位置等信息的好方法,某些数据必须重复,这很容易出错,但我很高兴我可以把处理我数据的所有代码塞进文档中,详细解释必要的部分,参考我描述问题出现原因的文档(单独章节),以及运行的所有参数,然后只需输入 make。希望这能有所帮助。