17

Could not find anything in the TeXBook, but this could be just me.

I guess it is always possible to open the file for reading, copy its content to another file, and then continue writing to that file. Finally, you could escape to shell to remove the original file and rename the newly generated file to the file you have just created.

3
  • 4
    I've wondered about this, and concluded that the only way is as you say to read everything in. The TeX \openout primitive simply does not allow appending. (Of course, LuaTeX is a different case).
    – Joseph Wright
    Feb 23, 2011 at 7:37
  • @Joseph: if you write this as an answer, I could mark it is as correct, just to tidy this up and close that question.
    – Yossi Gil
    Feb 24, 2011 at 0:08
  • 1
    Related question: "File copy without expansion" Mar 1, 2011 at 17:00

3 Answers 3

12

I agree with Joseph that TeX doesn't support appending to an existing file. However it is possible to read the existing content and write it together with the new content back to the file. This is of course much less efficient than simply appending content, but the only possible way in TeX.

The catchfile makes the reading of the existing content quite easy:

\documentclass{article}

\usepackage{catchfile}

\newwrite\appendwrite
\newcommand*\appendtofile[2]{%
    \begingroup
    \IfFileExists{#1}%
      {\CatchFileDef{\filecontent}{#1}{\endlinechar=`^^J\catcode\endlinechar=12\relax}}% keep existing end-of-lines
      {\let\filecontent\empty}%
    \immediate\openout\appendwrite=#1\relax
    \immediate\write\appendwrite{\filecontent #2}%
    \immediate\closeout\appendwrite
    \endgroup
}

\begin{document}

\appendtofile{\jobname.test}{First line}

First:\\\fbox{\input{\jobname.test}}

\appendtofile{\jobname.test}{Second line}

Second:\\\fbox{\input{\jobname.test}}

\end{document}

See also the answers to the question "File copy without expansion".

2
  • 1
    It seems that if the file contains /, this code does not work... It seems that the solution is to use instead \immediate\write\appendwrite{\unexpanded\expandafter{\filecontent} #2}
    – tobiasBora
    Apr 11, 2019 at 16:51
  • Note also that if the file contains #, each time one append something to the file the number of # is doubled. See tex.stackexchange.com/questions/631785/…
    – tobiasBora
    Jan 28, 2022 at 8:44
8

You could write what you want to append into a new file, and rewrite both files together at the end of the run. Something like the following.

\documentclass{minimal}
\newwrite\mywrite
\newcount\mycount

\makeatletter
\newcommand{\makeallother}{%
  \mycount0\relax
  \loop\ifnum\mycount<255\relax
  \catcode\mycount12\relax
  \advance\mycount by 1\relax
  \repeat
}
\newcommand*{\VerbatimFile}[2]{%
  \begingroup
  \newlinechar\endlinechar
  \makeallother
  \everyeof{\noexpand}%
  \immediate\openout\mywrite #1\relax
  \def\from##1{\immediate\write\mywrite{\@@input ##1 }}%
  #2\relax
  \immediate\closeout\mywrite
  \endgroup
}
\makeatother

\AtBeginDocument{\immediate\openout\mywrite\jobname.append\relax}
\AtEndDocument{%
  \immediate\closeout\mywrite
  \VerbatimFile{\jobname.output}{%
    \from{\jobname.tex}%
    \from{\jobname.append}%
  }%
}

\begin{document}

\newcommand{\test}[1]{\immediate\write\mywrite{#1}}
\test{abc}
\test{cde}

\end{document}
1
  • This answer is more resilient than the accepted one as it works also when the file contains #. I converted this answer into a function that just take a file and a string to append (like the accepted answer) here tex.stackexchange.com/questions/631785/…
    – tobiasBora
    Jan 28, 2022 at 12:21
7

I've wondered about this, and concluded that the only way is as you say to read everything in. The TeX \openout primitive simply does not allow appending. (Of course, LuaTeX is a different case). If you are going to read everything in, the e-TeX \readline primitive might be useful, as it reads everything detokenized.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .