扩展至文件内容的命令

扩展至文件内容的命令

我想制作一个可以扩展文件内容的宏。基本上就像

\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需要两个参数,第一个参数指定所需的数据moddatesize。第二个参数是文件名。

#!/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 的删除命令名为rmdel

    \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}

在此处输入图片描述

相关内容