2019 年更新

2019 年更新

假设我使用以下命令编译以下文件pdflatex -shell-escape test.tex

\documentclass{minimal}

\begin{document}

File listing is:

\immediate\write18{ls /usr}

\end{document}

这会将命令的输出发送ls /usr到报告/日志pdflatex(主要发送到stdout)。

我想利用两种情况:

  • ls /usr直接包含在文档中的输出(LaTeX 流)。
  • 的输出ls /usr成为的内容\newcommand(我的意思是,我希望在\newcommand第一次执行时执行脚本 - 并且在随后调用新命令时,shell 代码不应重新执行)。

我已经读完了如何从 LaTeX 执行 shell 脚本?,但我不确定这种“管道输入”是否可以应用于\newcommand

我也读过tex - 如何将 shell 输出保存到 LaTeX 中的变量?- 并且似乎应该使用 Tex 的文件 I/O;但我很不喜欢这样一个事实,即我仍然必须将脚本输出(实际上,在本例中是输出ls /usr)重定向到文件,然后读入它,将其作为命令的内容。

那么,有没有更简单的方法来实现我想要的(希望通过基于上述代码的示例来说明)?


编辑:嗯,我应该早点问一个问题:)我会尝试在这里进行编辑,尽管它可能会被忽略.. :)

我最初要求一个\newcommand仅在其定义时执行 shell 代码(即,在某种意义上它是“缓存的”);而 @egreg 的答案正是如此。但是 - 是否有可能有一个不同的\newcommand定义,这样每次调用这个新命令时,shell 命令都会重新执行?即执行类似的东西\@@input|"cat tempfile"(不能这样执行),tempfile调用之间会发生变化?

答案1

\documentclass{article}

\begingroup\makeatletter\endlinechar=\m@ne\everyeof{\noexpand}
\edef\x{\endgroup\def\noexpand\TeXpath{\@@input|"which tex" }}\x

\begin{document}
File listing is

{\catcode`_=12 \ttfamily
\input{|"ls /usr" }

}

\TeX{} is \TeXpath
\end{document}

我们必须使用\@@input(原始\input命令),因为\input在 LaTeX 中会进行赋值。的设置\endlinechar是为了避免在扩展中出现虚假空格\TeXpath

当 shell 转义处于活动状态并且原语\input找到 时|,它会接受以下 shell 命令的标准输出作为输入。

H. Oberdiek 应该有一个包可以做这种事。

笔记赋值是任何为控制序列或寄存器赋予含义或值的 TeX 操作。在\edef操作过程中,TeX 会扩展括号内找到的所有命令,直到只剩下不可扩展的标记,但不执行任何任务;相反,类似\catch=22(其中是计数寄存器的名称)的内容仍然完全不变。由于LaTeX 中\catch的定义是\input

\@ifnextchar\bgroup\@iinput\@@input

执行的隐式赋值\@ifnextchar将不会执行,并且两个都 \@input\@@input会被展开,这会导致彻底的灾难。相反,\input原语(LaTeX 保存为\@@input)是可扩展的,其扩展包括使 TeX 读取命名文件。当然,必须小心这个文件包含的内容,因为这也会被展开。因此,在执行此类操作时必须采取其他预防措施,具体取决于我们要执行的命令产生的标记的性质,并且此“解决方案”只是可能的“真实”应用程序的框架。


2019 年更新

几年后,情况发生了变化,并且出现了更好的方法。

例如,可以使用xparseexpl3改进代码:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\captureshell}{som}
 {
  \sdaau_captureshell:Ne \l__sdaau_captureshell_out_tl { #3 }
  \IfBooleanT { #1 }
   {% we may need to stringify the result
    \tl_set:Nx \l__sdaau_captureshell_out_tl
     { \tl_to_str:N \l__sdaau_captureshell_out_tl }
   }
  \IfNoValueTF { #2 }
   {
    \tl_use:N \l__sdaau_captureshell_out_tl
   }
   {
    \tl_set_eq:NN #2 \l__sdaau_captureshell_out_tl
   }
 }

\tl_new:N \l__sdaau_captureshell_out_tl

\cs_new_protected:Nn \sdaau_captureshell:Nn
 {
  \sys_get_shell:nnN { #2 } { } #1
  \tl_trim_spaces:N #1 % remove leading and trailing spaces
 }
\cs_generate_variant:Nn \sdaau_captureshell:Nn { Ne }
\ExplSyntaxOff

\begin{document}

\captureshell*[\TeXpath]{which tex} % we need to stringify it because of _

File listing is

{\ttfamily\captureshell{ls \jobname.*}\par}

\TeX{} is \texttt{\TeXpath}

\end{document}

在此处输入图片描述

如果用户没有通过-shell-escapeLaTeX 运行选项,我们可以添加一条错误消息。

也检查texosquery(需要 Java)。

答案2

这是一个简单的方法,使用我的bashful

\documentclass{article}
\usepackage[a6paper]{geometry}
\usepackage{bashful}

\begin{document}
\bash[script,stdout]
ls -F /usr
\END
\end{document}

从而产生 将 <code>ls</code> 的输出合并到 LaTeX 文档中

答案3

我认为这个片段对这个主题会有帮助。

https://gist.github.com/w495/7328b76e76aee49657e0bd7a3b46c870

% !TeX encoding = UTF-8
\ProvidesPackage{bashline}[2016/10/24 v. 0.1]

\makeatletter
    \newcommand{\bashline@file@name}[1]{%
        /tmp/${USER}-${HOSTNAME}-\jobname-#1.tex%
    }
    \newread\bashline@file
    \newcommand{\bashline@command@one}[2][tmp]{%
        \immediate\write18{#2 > \bashline@file@name{#1}}
        \openin\bashline@file=\bashline@file@name{#1}
        % The group localizes the change to \endlinechar
        \bgroup
            \endlinechar=-1
            \read\bashline@file to \localline
            % Since everything in the group is local, 
            % we have to explicitly make the assignment global
            \global\let\bashline@result\localline
        \egroup
        \closein\bashline@file
        % Clean up after ourselves
        \immediate\write18{rm \bashline@file@name{#1}}
        \bashline@result
    }
    \newcommand{\bashline@command@many}[2][tmp]{%
        \immediate\write18{#2 > \bashline@file@name{#1}}
        \openin\bashline@file=\bashline@file@name{#1}
        % The group localizes the change to \endlinechar
        \newcount\linecnt
        \bgroup
            \endlinechar=-1
            \loop\unless\ifeof\bashline@file 
                \read\bashline@file to \localline%
                \localline
                \newline
            \repeat
        \egroup
        \closein\bashline@file
        % Clean up after ourselves
        \immediate\write18{rm \bashline@file@name{#1}}
    }
    \newcommand{\bashline}[2][tmp]{%
        \bashline@command@one[#1]{#2}%
    }
    \newcommand{\bashlines}[2][tmp]{%
        \bashline@command@many[#1]{#2}%
    }
\makeatother

\newcommand{\urandomstring}[1]{%
    \bashline{cat /dev/urandom | tr -dc "A-Za-z0-9" | fold -c#1 | head -1}%
}

\newcommand{\bashdate}{%
    \bashline{date --iso-8601}%
}

\newcommand{\bashdatetime}{%
    \bashline{date --iso-8601=seconds}%
}

\newcommand{\commit}{%
    \bashline{git describe --dirty }%
}

\newcommand{\commitlog}{%
    \bashline{git log -1 --oneline}%
}

\newcommand{\branch}{%
    \bashline{git describe --all}%
}

\endinput

它基于Antal 的回答 在“如何将 shell 输出保存到 LaTeX 中的变量?”中。例如,检查\urandomstring。每次调用时都会生成新的随机字符串。另请参阅\bashlines宏。它对我来说就像本机一样工作bash

下面是一个例子: https://www.sharelatex.com/project/580e8926fe7b0dfd2ef8ae52

正如您所看到的,\bashdatetime每次给出不同的纳秒。

答案4

你可以使用我的包裹iexec

\documentclass{minimal}
\usepackage{iexec}
\begin{document}
File listing is:
\iexec{ls /usr}
\end{document}

相关内容