26

How can one write the body of an environment verbatim to an external file. I tried the following but get problems if the body contains undefined macros (error) or e.g. a % (disappears in the output).

Try 1
Using environ to get the environment content.

\ProvidesPackage{tofile1}

\RequirePackage{environ}

% counter for external files
\newcounter{TF@File}

\newwrite\TF@out

\NewEnviron{tofile}{%
    \stepcounter{TF@File}
    \edef\TF@outFile{ext-\jobname-\theTF@File.tex}
    \typeout{Name: \TF@outFile}
    \immediate\openout\TF@out=\TF@outFile
    \typeout{\TF@outFile\space opened}
    \immediate\write\TF@out{\BODY}
    \typeout{\TF@outFile\space written}
    \immediate\closeout\TF@out
    \typeout{\TF@outFile\space closed}
}

% test.tex
\documentclass{article}

\usepackage{tofile1}

\begin{document}
Test

\begin{tofile}
That's {a short Test}
which works
but ignores line breaks
\end{tofile}

Text

\begin{tofile}
\xx That's {another Test}%
This one doesn't work ...
it gives an error because \xx is undefined
\end{tofile}
\end{document}

This one creates the two desired files ext-test-1.tex and ext-test-1.tex but linebreaks are ignored and an undefined control sequence (\xx) causes an error. Furthermore the percent char is interpreted as comment not as simple character. I guess come catcode magic could help.

Try 2
Using the filecontents package.

\ProvidesPackage{tofile2}

\RequirePackage{environ}
\RequirePackage{filecontents}

% counter for external files
\newcounter{TF@File}

\newwrite\TF@out

\newenvironment{tofile}{%
    \stepcounter{TF@File}
    \edef\TF@outFile{ext-\jobname-\theTF@File.tex}
    \begin{filecontents}{\TF@outFile}
}{
    \end{filecontents}
}

Same test file as above ...

This one creates only the first file ext-test-1.tex and the an error occurs because the end of filecontens can’t be determined correctly.

Is there another way do do this, maybe a package?

5
  • 1
    The reason why the filecontents solutions doesn't work is that it looks for \end{filecontents} verbatim and can't find it inside \end{tofile}. In general you can't include verbatim macros or environments in other macros or environments. Dec 10, 2011 at 11:43
  • @MartinScharrer: Yea, I thought so …
    – Tobi
    Dec 10, 2011 at 14:58
  • have a look at the answers package too
    – cmhughes
    Dec 10, 2011 at 16:14
  • About writing % into a file see also tex.stackexchange.com/q/34390/6865
    – Stephen
    Dec 10, 2011 at 17:13
  • Depends on the use case, scontents or filecontentsdef to store the content in memory instead of an external file may be better. //// The filecontents environment is actually extensible, but you need \filecontents ... \endfilecontents -- example. Also the starred version does not include the header -- see section 8.11 (page 62) of latex2e.pdf.
    – user202729
    Nov 11, 2021 at 7:35

2 Answers 2

23

You can look at the definition of verbatimwrite in verbatim.sty or use the facilities provided by the fancyvrb package:

\documentclass{article}

\usepackage{fancyvrb}
\newenvironment{tobiwrite}[1]
  {\typeout{Writing file #1}\VerbatimOut{#1}}
  {\endVerbatimOut}

\begin{document}
\begin{tobiwrite}{tobi.txt}
a

b
c
\end{tobiwrite}
\end{document}

Instead of \typeout you can do whatever you want (for example, checking whether the file already exists with \IfFileExists).

2
  • @MartinScharrer, egreg: Thank you both seems like both answers math my questions. Could you pleas figure out the differences if there are any?
    – Tobi
    Dec 10, 2011 at 14:59
  • @Tobi A very late answer, but it seems I missed seeing your comment. What are the differences? Well, listings loads fancyvrb, first of all. However, the definition of \lst@BeginWriteFile is different from \VerbatimOut, since many more settings are necessary for supporting the full power of listings. If you're only interested in verbatim copy, \VerbatimOut is more efficient.
    – egreg
    Feb 2, 2013 at 16:28
13

It is possible to use internal macros from the listings package to do this.

\documentclass{article}

\makeatletter
\RequirePackage{listings}
\lst@RequireAspects{writefile}

\lstnewenvironment{yourenv}{%
  % some code before
  % including \lstset{..}
  % Write file to given filename
  \lst@BeginWriteFile{\jobname.txt}%
}
{%
  \lst@EndWriteFile% closes output file
}
\makeatother

\begin{document}
% ...    
\end{document}

Compare with the similar code I provided in my answers to the following questions:
LaTeX documentation with verbatim environment
How to use a \write command inside a \newenvironment ?

3
  • Hi, is it possible to remove an indention (white space at the beginning of the line) while writing the file?
    – Tobi
    Nov 10, 2014 at 22:55
  • @Tobi: Have a look at listings gobble option. Not sure if it works with writting files. Nov 11, 2014 at 17:43
  • Hm … Seems like the gobble function is hard wired with the user level macros and can’t be used with \lst@BeginWriteFile
    – Tobi
    Nov 13, 2014 at 12:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.