27

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?

Tobi
  • 56,353
  • 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. – Martin Scharrer Dec 10 '11 at 11:43
  • @MartinScharrer: Yea, I thought so … – Tobi Dec 10 '11 at 14:58
  • have a look at the answers package too – cmhughes Dec 10 '11 at 16:14
  • About writing % into a file see also http://tex.stackexchange.com/q/34390/6865 – Stephen Dec 10 '11 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 '21 at 07:35

2 Answers2

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).

egreg
  • 1,121,712
  • @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 '11 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 02 '13 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 ?

Martin Scharrer
  • 262,582
  • Hi, is it possible to remove an indention (white space at the beginning of the line) while writing the file? – Tobi Nov 10 '14 at 22:55
  • @Tobi: Have a look at listings gobble option. Not sure if it works with writting files. – Martin Scharrer Nov 11 '14 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 '14 at 12:44