我想制作一个可以扩展文件内容的宏。基本上就像
\def{\mycommand}{\input{file.txt}} % file.txt contains "Hello%"
\anothercommand{\mycommand}
应该完全等同于:
\def{\theircommand}{Hello}
\anothercommand{\theircommand}
该文件应input
在使用宏时进行 ted,而不是在定义宏时进行 ted(因为我是在动态生成它,并且它可能会在整个文档中发生变化)。我这样做的原因是因为我想模拟\pdf@filemoddate
,这是我需要的,但它在我工作中使用的旧 pdfLaTeX 版本中不可用。这是一个非常好的、几乎是最小的、非工作示例,其中包含更多信息:
\documentclass{article}
\makeatletter
% This part is from epstopdf. I can't change this.
% It just takes a string like D:20090114124316Z and
% formats it as a date.
% --------------------------------------------------
\def\ETE@Date#1\@nil{%
\ifx\\#1\\%
\else
\ETE@@Date#1\@nil
\fi
}%
\def\ETE@@Date#1:#2#3#4#5#6#7#8#9{%
date: #2#3#4#5-#6#7-#8#9 %
\ETE@@Time
}%
\def\ETE@@Time#1#2#3#4#5#6#7\@nil{%
#1#2:#3#4:#5#6%
}%
\makeatother
% --------------------------------------------------
% some commands of increasing complexity
% this one just expands to the string I want out:
\def\blahone{D:20090114124316Z}
% this is the core functionality that I'm missing:
\def\blahthree{\input{temp.txt}}
% It seems to be not a problem with 'input', but an
% expansion problem, as this doesn't work either:
\def\blahtwo{\blahone}
% This is very close to the actual command I want to define
\def\myfiledate#1{%
% in reality I call a little script with #1 here
\immediate\write18{cat temp.txt > temp2.txt}%
\input{temp2.txt}%
\immediate\write18{rm temp2.txt}%
}
\begin{document}
\makeatletter
All macros work on their own. There must not be spaces around the Xs: \\
X{\blahone}X \\
X{\blahtwo}X \\
X{\blahthree}X \\
X{\myfiledate{test.tex}}X \\
However, I can't use the results of the more complex ones as arguments: \\
\expandafter\ETE@Date\blahone\@nil \\
% doesn't work:
%\expandafter\ETE@Date\blahtwo\@nil \\
% need the 'romannumeral' trick to force full expansion:
\expandafter\ETE@Date\romannumeral-`X\blahtwo\@nil \\
% but that isn't an option. I have code that calls
% \expandafter\ETE@Data\MACRO\@nil
% and I can only replace \MACRO, not change that code!
% doesn't work at all, no matter how many expandafters I try:
%\expandafter\ETE@Date\blahthree\@nil \\
%\expandafter\ETE@Date\filedate\@nil \\
\makeatother
\end{document}
文件 test.txt 仅包含
D:20090114124316Z%
(末尾没有换行符)。
更新(我想要实现的目标):我有一个包含大量图形(eps)的大型文档。使用 pdfLaTeX 编译时,epstopdf 只会根据需要将 eps 文件转换为 pdf。但是,在我工作的 PC 上,缺少 pdffilemoddate 命令,epstopdf 无法判断 eps 文件是否已被修改,因此它每次都坚持再次转换所有图形,这需要很长时间。我希望有一个\def\pdf@filemoddate#1{...}
可以放在标题中的替代品,并允许 epstopdf 按预期运行。
答案1
当然你可以使用包catchfile
,请参阅大卫·卡莱尔的评论。
但是你已经有一个脚本了。只需稍微扩展一下就可以输出 TeX 代码而不是裸数据。例如,使用文件名作为参数调用该脚本,它会写入一个temp.tex
包含以下内容的文件:
\mypdffilemoddate{<file>}{<year>}{<month>}{<day>}{<hour>}{<min>}{<sec>}{<timezone>}
具体例子:
\mypdffilemoddate{foobar.tex}{2013}{05}{30}{19}{00}{00}{Z}
宏\mypdffilemoddate
是根据您的需要定义的:
\newcommand*{\mypdffilemoddate}[8]{%
% #1: file name
% #2: year
% #3: ... (see above)
%
% Examples, what can be done:
% * date formatting
% * storing the file date:
\@namedef{mydate@#1}{<date>}
}
如果文件日期被存储,则稍后可以使用:
\@nameuse{我的日期@}
脚本执行后,文件输入
\input{temp}
或者
\InputIfFileExists{temp}{<file exists and is read>}{<file does not exist>}
文件temp.tex
被读取并\mypdffilemoddate
调用。
答案2
epstopdf
因为关键问题似乎是对具有所有功能的包的支持,特别是缺少(和)update
的旧版 pdfTeX 的选项。\pdffilemoddate
\pdffilesize
正如问题中所述,缺失的功能是使用\immediate\write18
(shell escape feature)实现的。运行脚本来提供缺失的数据、文件日期和文件大小。输出被写入文件并在 TeX 运行中读入。
使用最新的 pdfTeX,还有一个管道功能可以避免使用临时文件。还\pdfshellescape
可以报告 shell 转义功能的状态。如果未启用 shell 转义,则可以使用该功能发出警告或错误。由于使用的是较旧的 pdfTeX,因此以下示例使用临时文件,并且必须启用 shell 转义功能。
以下 Perl 脚本pdffiletools.pl
需要两个参数,第一个参数指定所需的数据moddate
或size
。第二个参数是文件名。
#!/usr/bin/env perl
use strict;
$^W=1;
my $prg_kpsewhich = 'kpsewhich';
my $kpsewhich_engine = 'pdftex';
my $kpsewhich_progname = 'pdflatex';
my $kpsewhich_format = 'tex';
my $syntax = "Syntax: $0 <action> <file>\n";
@ARGV == 2 or die $syntax;
my $action = shift;
my $arg_file = shift;
my @cmd = (
$prg_kpsewhich,
"-engine=$kpsewhich_engine",
"-progname=$kpsewhich_progname",
"-format=$kpsewhich_format",
);
my $file = `@cmd '$arg_file'`;
if ($? == -1) {
die "!!! Error: Failed to execute ($prg_kpsewhich): $!\n";
}
elsif ($? & 127) {
die sprintf "!!! Error: Child ($prg_kpsewhich) died "
. "with signal %d %s coredump!\n",
($? & 127), ($? & 128) ? 'with' : 'without';
}
elsif ($? != 0) {
die printf "!!! Error: Child ($prg_kpsewhich) exited with value %d!\n",
$? >> 8;
}
chomp $file;
-f $file or die "!!! Error: File not found ($arg_file)!\n";
if ($action eq 'moddate') {
my ($sec, $min, $hour, $mday, $mon, $year) = gmtime((stat($file))[9]);
printf "D:%04d%02d%02d%02d%02d%02dZ",
$year + 1900, $mon + 1, $mday, $hour, $min, $sec;
exit(0);
}
if ($action eq 'size') {
print ((stat($file))[7]);
exit(0);
}
die "!!! Error: Unknown action ($action)!\n";
__END__
评论:
- 该文件是通过设置一些选项来找到的,以模仿在和中查找文件的
kpsewhich
方式。pdflatex
\pdffilemoddate
\pdffilesize
- 为简单起见,文件日期使用 GMT 作为时区。更豪华的版本将使用本地日期并附加时区规范。
- 结果以退出代码 0 打印到标准输出。
- 错误被打印到标准错误流并设置退出代码。
- 在 Linux 下测试。
以下测试文件使用上述脚本添加旧版 pdfTeX 包所缺少的功能epstopdf
:
\documentclass{article}
\usepackage{pdftexcmds}
\makeatletter
% \let\pdf@filemoddate\relax% testing with newer pdfTeX
% \let\pdf@filesize\relax% testing with newer pdfTeX
\@ifundefined{pdf@filemoddate}{%
\newcommand*{\pdf@filemoddate}[1]{%
\romannumeral-`\x
\@ifundefined{filemoddate@#1}{}{\@nameuse{filemoddate@#1}}%
}%
\newcommand*{\pdf@filesize}[1]{%
\romannumeral-`\x
\@ifundefined{filesize@#1}{}{\@nameuse{filesize@#1}}%
}%
\RequirePackage{graphicx}\relax
\RequirePackage{epstopdf}\relax
\RequirePackage{catchfile}\relax
\newcommand*{\setpdf@filemoddate}[1]{%
\immediate\write18{%
./pdffiletools.pl moddate '#1'>tmp-pdffiletools.txt%
}%
\CatchFileEdef{\setpdf@catch}{tmp-pdffiletools.txt}{%
\@makeother\D
\@makeother\:%
\@makeother\Z
\@makeother\+%
\@makeother\-%
\@makeother\'%
\endlinechar=-1\relax
}%
\expandafter\let\csname filemoddate@#1\endcsname\setpdf@catch
}%
\newcommand*{\setpdf@filesize}[1]{%
\immediate\write18{%
./pdffiletools.pl size '#1'>tmp-pdffiletools.txt%
}%
\CatchFileEdef{\setpdf@catch}{tmp-pdffiletools.txt}{%
\endlinechar=-1\relax
}%
\expandafter\let\csname filesize@#1\endcsname\setpdf@catch
}%
%
\newcommand*{\org@ETE@Make}{}%
\let\org@ETE@Make\ETE@Make
\renewcommand*{\ETE@Make}[2]{%
\ifETE@update
\setpdf@filemoddate{#1}%
\setpdf@filemoddate{#2}%
\fi
\org@ETE@Make{#1}{#2}%
}%
%
\newcommand*{\org@ETE@DefCommandLine}{}%
\let\org@ETE@DefCommandLine\ETE@DefCommandLine
\renewcommand*{\ETE@DefCommandLine}{%
\setpdf@filemoddate{\SourceFile}%
\setpdf@filemoddate{\OutputFile}%
\setpdf@filesize{\SourceFile}%
\setpdf@filesize{\OutputFile}%
\org@ETE@DefCommandLine
}%
}{%
\usepackage{graphicx}%
\usepackage{epstopdf}%
}
\makeatother
\begin{document}
\includegraphics{test-image.eps}
\end{document}
评论:
- 需要启用 Shell 转义 (
pdflatex -shell-escape
)。 - 该脚本被放入当前目录,因此被调用为
./pdffiletools.pl
。如果它被安装pdffiletools
在环境变量的目录中PATH
,那么它被调用为pdffiletools
。 \setpdf@filemoddate{<file>}
并\setpdf@filesize{<file>}
调用脚本并将结果存储在宏\filemoddate@<file>
和中。这允许和\filesize@<size>
的可扩展定义。\pdf@filemoddate{<file>}
\pdf@filesize{<file>}
- 这个
\romannumeral
技巧保留了包的通用属性pdftexcmds
,\pdf@filemoddate
并且\pdf@filesize
可以在两个扩展步骤中完成扩展。 - 而不是方法我的其他答案我已经使用包
catchfile
来为该方法提供用例示例。 - 原始字符串
\pdffilemoddate
和\pdffilesize
返回字符串,其中字符的 catcode 为“other”(12)。这就是在 setup 部分中进行 catcode 设置的原因\CatchFileEdef
。 - 这里可以使用和。后者的实现更简单、更简短
\CatchFileDef
。\CatchFileEdef
临时文件可以被自动删除,例如(Linux 的删除命令名为,Windows 的删除命令名为
rm
)del
:\usepackage{atveryend} \AtVeryEndDocument{% \IfFileExists{tmp-pdffiletools.txt}{% \immediate\write18{rm tmp-pdffiletools.txt}% }{}% }
答案3
我认为该readarray
包可以满足您的要求。为了展示它,我使用它的\readdef
命令输入文件内容并将其放入令牌中。然后我将其传递给字符串解析器以抓取其中的部分。
\documentclass{article}
\usepackage{readarray}
\usepackage{stringstrings}
\begin{document}
\readdef{temp.txt}{\blahfour}
X{\blahfour}X
\substring{\blahfour}{3}{6}
\substring{\blahfour}{$-6}{$}
\end{document}