
Eu gostaria de escrever algumas macros simples para lidar com coisas como referência cruzada, para usá-las em TeX simples. (Sei que já existem macros dedicadas a isso, como as presentes no Eplain, mas gostaria de tentar algo diferente sozinho.) Portanto, preciso saber como ler um arquivo e como escrever em um arquivo. Quais são as primitivas do TeX que fazem essas coisas? Como eles funcionam?
Outra pergunta: o TeX pode 'chamar' outros programas enquanto está em execução? Quero dizer: existe no TeX um equivalente à função de sistema presente na linguagem C?
Responder1
TeX tem as primitivas \read
e \write
para ler e escrever em arquivos e, claro, \input
para inserir um arquivo inteiro 'aqui'. Se você observar, por exemplo, o mecanismo de referência cruzada do LaTeX é usado, \write
mas evita o uso \read
(linha por linha) em favor do uso \input
de arquivos secundários projetados adequadamente.
Como \input
é fácil de entender, vamos nos concentrar em \read
e \write
. Ambos funcionam em um fluxo de arquivos, que recebe um número, mas geralmente é alocado usando \new...
. Por exemplo
\newread\myread
\openin\myread=myinput %
\newwrite\mywrite
\immediate\openout\mywrite=myoutput %
configurará uma chamada de leitura \myread
e uma chamada de gravação \mywrite
. Observe que usei \immediate
com o \write
: devido à natureza assíncrona do construtor de páginas TeX, você precisa ter certeza de que as \write
operações acontecem no local 'correto'. (Mais sobre isso abaixo.)
Com dois fluxos abertos podemos, por exemplo, escrever na saída. Se fizermos duas gravações, uma 'agora' e outra 'atrasada'
\def\foo{a}
\immediate\write\mywrite{\foo}
\write\mywrite{\foo}
\def\foo{b}
Hello
\bye
o resultado é myoutput.tex
a leitura
a
b
Isso porque \write\mywrite
produz um whatsit que só é executado quando uma página é enviada. Isso é útil se, por exemplo, o que você precisa escrever contiver um número de página, pois isso só é conhecido durante o estágio de saída. Observe também que \write
age como \edef
: tudo é expandido, a menos que você evite usar \noexpand
ou um toks
. Observe, entretanto, que esta expansão é realizada no momento em que a \write
operação é realmente executada, portanto, é necessário garantir que as macros tenham definições adequadas ao usar um arquivo atrasado \write
.
A \read
primitiva lê uma linha por vez (a menos que as chaves não correspondam) e tokeniza da maneira normal do TeX. Você pode fazer um loop em um arquivo, uma linha por vez, usando o \ifeof
teste on \myread
, mas como eu disse, geralmente é mais fácil simplesmente criar \input
um arquivo contendo referências cruzadas.
Se você quiser fazer uma chamada de sistema, o TeX 'puro' realmente não ajuda. No entanto, o Web2c tem há muito tempo um 'stream' especial para permitir a fuga para o sistema: \write18
. Este é um risco à segurança e, portanto, como padrão, apenas um conjunto restrito de comandos é permitido nesse tipo de fuga. Você pode fazer por exemplo
pdftex --shell-escape myfile
para permitir todas as fugas: o risco, se você mesmo escreveu todo o código, é apenas cometer uma bagunça! Fazer um \write18
não retorna nada ao TeX: você precisará organizar a leitura do resultado de alguma forma, provavelmente usando \read
um arquivo secundário.
Conforme observado em um comentário, uma extensão de sintaxe adicional disponível é \input|"<command>"
. Isso é novamente restrito, \write18
mas fornece um método expansível para obter entradas de comandos shell.
Responder2
Outra questão especial ao tratar de referências cruzadas via arquivo externo é a ordem de leitura, abertura, escrita e fechamento deste arquivo. O esquema básico é:
\newwrite\fileout % allocations of the file number
\def... ... all macros which can be used in the file must be defined
\input file ... input only when file exists (the file doesn't exist at the first run)
... this input stores the data from file into internal macros
\immediate\openout\fileout=file ... this removes the data from file
... the file has zero length now
... document ... macros used in the document write (typically not immediately)
data to the file
\end ... this closes the file automatically,
but if you need to use the data from the file at the end of the document
(for example the TOC at the end of the document):
\vfil\eject
\immediate\closeout\fileout
\input file ... this creates the TOC at the end of the file.
\end
O esquema acima mostra que você precisa criar uma macro que leia o arquivo somente \input
quando este arquivo existir. Você pode usar a seguinte \softinput
macro para tais fins:
\newread\testin
\def\softinput #1 {\let\next=\relax \openin\testin=#1
\ifeof\testin \message{Warning: the file #1 does not exist}%
\else \closein\testin \def\next{\input #1 }\fi
\next}