2

We know \IfFileExists{<file>}{<yes>}{<no>}.

How can I create a command \IfFileHasChanged{<file>}{<yes>}{<no>}
for a file which has changed (and does exist!).

I mean it this way:

  • If I have myfile.txt with This is my file. it should be written in the first (with filecontents).

  • If I change myfile.txt to Now this is my file... it should be new-written.

  • else: do not write new.

Maybe this has something to do with the "timestamp".

\begin{filecontents*}[overwrite]{myfile.txt}
This is my file.
\end{filecontents*}

\documentclass{article} \begin{document} \section{IfFileExists} \IfFileExists{myfile.txt}{\input{myfile.txt}}{Does not exist!}

\section{IfFileHasChanged} \end{document}

cis
  • 8,073
  • 1
  • 16
  • 45
  • changed relative to which state? – Ulrike Fischer Aug 05 '20 at 16:28
  • Edit............. – cis Aug 05 '20 at 16:42
  • sorry your description doesn't make any sense to me. – Ulrike Fischer Aug 05 '20 at 16:48
  • I do not understand also what means "changed" for you. Changed since last time you compiled the main document? Since a fixed date/time? Since the last time you read it in the same LaTeX run? – Rmano Aug 05 '20 at 18:22
  • I mean, if the content has changed. I am sorry, if I knew how to get there I would do it myself. – cis Aug 05 '20 at 18:40
  • 2
    @cis, still --- changed from what? Changed since the last run? Since the last hour? Since the start of the PC? I have guessed a possible thing, see my answer... – Rmano Aug 05 '20 at 20:46
  • Do you wish to find out whether the body of the filecontents*-environment contains the same set of characters as is stored in the file (if existing) which is to be created from that filecontents*-environment? If this is what you wish, you can have LaTeX read both the body of the environment and the file under verbatim-catcode-régime and compare. E.g., read and compare linewise by storing lines as temporary macros and doing \ifx-comparison, or (if it is only about small snippets of text) read and store the entire things as temporary macros and doing \ifx-comparison. – Ulrich Diez Aug 07 '20 at 11:49
  • You say: If I have myfile.txt with This is my file. it should be written in the first (with filecontents). The content of myfile.txt will be the single line This is my file. If I change myfile.txt to Now this is my file... it should be new-written. When you say "change" you mean editing the file myfile.txt? What do you mean by "new-written"? What shall be the content of myfile.txt when that ominous "new-writing" has taken place? What about changes of attributes/rights/time-stamps etc - i.e. changes that do not affect the content of the file? – Ulrich Diez Aug 07 '20 at 23:03
  • @UlrichDiez I'll try to explain it this way: I have a simple raw data table. From this I create an auxiliary file for further processing. I want the complex process of creating that auxiliary file to only happen if something has changed in the raw data. See here: https://tex.stackexchange.com/a/557508/46023 – cis Aug 08 '20 at 09:43
  • @cis Your raw data file is a comma-separated-value-list (csv). Did you consider using the package datatool? Using this package you probably don't need an auxiliary file as you can directly load text-files containing such csv-lists as database for iterating on the database's entries and sorting things out etc. Or during the process of creating the auxiliary file store the md5-sum of your raw-data within the auxiliary-file and have the auxiliary-file recreated when the md5-sum stored in the auxiliary file differs from the md5-sum of the raw-database-file itself. – Ulrich Diez Aug 08 '20 at 11:49
  • @cis Alternatvely - if the raw-data is not all too large: The process which creates the auxiliary-file can probably copy the raw-data into the auxiliary-file, nested into something like my DifferentFileContents-environment. When LaTeX processes the auxiliary file, the environment will compare its content to the raw-data-file. If things are not the same, the process of creating the auxiliary-file anew is to be initiated. This way you use the content of the raw-data-file instead of the md5-sum of the raw-data-file. If you are interested I can elaborate on this in another answer. – Ulrich Diez Aug 08 '20 at 12:01

3 Answers3

4

I suppose that the situation is the following. We have a LaTeX file, call it filechanged.tex, that is run with pdflatex filechanged. In the same directory, I have another file, myfile.txt.

When I run pdflatex filechanged, I want to do different processing if myfile.txt has changed since the previous run or not.

My solution:

  1. It is based on @Skillmon suggestion here and @egreg code here;

  2. it will write on the .aux file (which is automatically read at the start of the run) the value of the MD5 checksum of the file myfile.txt;

  3. it will check if it has changed since last run and do different things if yes or not;

  4. it needs a quite recent LaTeX distribution (no idea when the \file_get_mdfive_hash:nN was introduced)

  5. it is my first LaTeX3 program, so it's probably full of errors.

Seems to work here... So you have two files, the first one is mytext.txt

something here

and then the main file:

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
    \cs_new:Npn \dobold #1
    {
        \textbf{#1}
    }
    \str_new:N \g_myfile_name
    \str_gset:Nn \g_myfile_name {myfile.txt}
    \str_new:N \g_myfile_old_mdfive
    \str_new:N \l_myfile_mdfive
    \cs_new:Npn \getmdfive
    {
        \file_get_mdfive_hash:nN {\str_use:N \g_myfile_name} \l_myfile_mdfive
        \str_use:N \l_myfile_mdfive
    }
    \cs_new:Npn \getoldmdfive
    {
        \str_use:N \g_myfile_old_mdfive
    }
    \cs_new:Npn \IfMyfileChanged #1 #2
    {
        \str_if_eq:NNTF \g_myfile_old_mdfive \l_myfile_mdfive {#2} {#1}
    }
    \AtEndDocument
    {
        \iow_now:cx { @auxout }
        {
            \token_to_str:N \ExplSyntaxOn
            ^^J
            \str_gset:Nn \token_to_str:N \g_myfile_old_mdfive {\str_use:N \l_myfile_mdfive}
            ^^J
            \token_to_str:N \ExplSyntaxOff
        }
    }
\ExplSyntaxOff

\begin{document}

MD5 was \getoldmdfive

MD5 is now \getmdfive

Changed? \IfMyfileChanged{Yes, it has changed}{No, it's the same as before}

\end{document}

Rmano
  • 40,848
  • 3
  • 64
  • 125
  • 1
    Nice first attempt! Just some nitpicking: macros that aren't expandable (read: blow up in an \edef) need to be protected, so \dobold and \getmdfive need to be defined with \cs_new_protected:Npn. Names of variables should indicate what's their type. For instance, \g_myfile_name should be \g_myfile_name_str. Instead of ^^J you can use \iow_newline:. \file_get_mdfive_hash:nN was introduced in 2017-07-11 (see its entry in interface3). Other than that, nice answer :-) – Phelype Oleinik Aug 05 '20 at 20:59
  • @PhelypeOleinik thanks (and blush I forgot to remove the \dobold thing). I admit I am still struggling with the non-expandability thing... – Rmano Aug 05 '20 at 21:04
  • A macro is only safely expandable if all the macros in its definition are expandable. You can find out if an expl3 macro is expandable by looking at interface3.pdf: if it's marked with a ⭑ it is expandable. For example, page 66 of interface3 documents \str_use:N with a ⭑, which means \getoldmdfive can be expandable (it's not mandatory). On the other hand, on page 169 the documentation of \file_get_mdfive_hash:nN doesn't have a ⭑, so it isn't expandable, neither is \getmdfive, so it has to be protected. – Phelype Oleinik Aug 05 '20 at 21:27
  • More generally (non-expl3), a macro is expandable if its definition contains only other expandable macros and/or expandable primitives (there's a list here). As a rule of thumb, any assignment (\def, \let, etc.) is not expandable: it's easier to remember those that are expandable: mostly conditionals (\if), expansion control primitives (\expandafter, \the, \csname,...) some e-TeX primitives like \unexpanded and \detokenize (and \expanded) and a couple of other less-used ones. – Phelype Oleinik Aug 05 '20 at 21:33
  • @PhelypeOleinik 1000 thanks! – Rmano Aug 05 '20 at 21:35
  • Yes, works good. Could you make that a command \IfFileHasChanged{<filename>}{<yes>}{<no>}? Similar to the known command \IfFileExists{<filename>}{<yes>}{<no>} – cis Aug 06 '20 at 07:19
  • Extending it to more than one file seems quite complex... I have to use associative arrays or thing like that. Will think about it. – Rmano Aug 06 '20 at 07:51
  • @PhelypeOleinik Is there some rule for documenting the amount of expansion-steps that need to be triggered with an expandable expl3-macro for obtaining the result? (When doing non-expl3-stuff, I often use \romannumeral0-expansion in order to reduce the amount of expansion-steps that need to be triggered to 1 or 2. In situations where you need control over expansion it is good to know the amount of expansions steps that need to be triggered. With expl3 by now I often look things up in the sources or via \show which is sort of painful.) – Ulrich Diez Aug 07 '20 at 11:40
  • @UlrichDiez Some macros (mostly performance-critical ones)have the number of required expansion steps documented. For example, \char_generate:nn and \prg_replicate:nn are documented to require two expansion steps (one step for the macro, one step for the \romannumeral inside them, and one step for the Dark Lord on his dark throne ;-). But most other macros don't have an explicit number of required expansion steps, mostly because expl3 provides the f expansion type type, so you (usually) don't need to worry about that. – Phelype Oleinik Aug 07 '20 at 12:27
4

Checking if a file has changed, as Ulrike says, requires to compare it against some previous state of the file.

Here's an implementation using roughly the same method as in Rmano's answer of storing the MD5 sum of the file in the .aux file, and also storing one MD5 per file in a property list, so that you can have multiple files.

\IfFileChangedTF checks if a file has changed relative to a previously known state by querying the MD5 of the file, and comparing it to the previous known value, and returns <false> or <true> accordingly. The first time a file is checked, no MD5 is known, so the conditional returns <true> as well.

The conditional has side effects, however. When the <true> branch is taken, the property list is updated with the new MD5 sum, which means that two consecutive runs of the command may yield a different result. This is especially true in the first run: when you first execute \IfFileChangedTF{some-file}, the command doesn't know some-file, so it stores the MD5 sum and returns <true>. The next time you run \IfFileChangedTF{some-file}, however, the file is already known, so if it didn't change the conditional returns <false>.

Also, since the state is stored in the .aux, before \begin{document} the first time you call \IfFileChangedTF for a file will always return <true>.

Running your sample document once produces (one “Didn't change”):

enter image description here

And running it again produces (two “Didn't change”):

enter image description here

\begin{filecontents*}[overwrite]{myfile.txt}
This is my file.
\end{filecontents*}

\documentclass{article} \usepackage{xparse} \pagestyle{empty} \ExplSyntaxOn \prop_new:N \g__cis_file_mdfive_prop \tl_new:N \l__cis_tmpa_str \tl_new:N \l__cis_tmpb_str \NewDocumentCommand \IfFileChangedTF { m +m +m } { \cis_file_if_changed:nTF {#1} {#2} {#3} } \prg_new_protected_conditional:Npnn \cis_file_if_changed:n #1 { T, F, TF } { \file_if_exist:nTF {#1} { \file_get_mdfive_hash:nN {#1} \l__cis_tmpb_str \prop_get:NnNTF \g__cis_file_mdfive_prop {#1} \l__cis_tmpa_str { \str_if_eq:NNTF \l__cis_tmpa_str \l__cis_tmpb_str { \prg_return_false: } { __cis_mdfive_update:nN {#1} \l__cis_tmpb_str \prg_return_true: } } { __cis_mdfive_update:nN {#1} \l__cis_tmpb_str \prg_return_true: } } { \msg_error:nnn { cis } { file-not-found } {#1} } } \makeatletter \cs_new_protected:Npn \cis@mdfive@update #1 #2 { \prop_gput:Nnx \g__cis_file_mdfive_prop {#1} {#2} } \cs_new_protected:Npn \cis@mdfive@save #1 #2 { \iow_now:Nx @auxout { \exp_not:N \cis@mdfive@update {#1} {#2} } } \cs_new_protected:Npn __cis_mdfive_update:nN #1 #2 { \cis@mdfive@update {#1} {#2} } \AtEndDocument { \prop_map_inline:Nn \g__cis_file_mdfive_prop { \cis@mdfive@save {#1} {#2} } } \makeatother \msg_new:nnn { cis } { file-not-found } { File~'#1'~not~found. } \ExplSyntaxOff

\begin{document} \section{IfFileExists} \IfFileExists{myfile.txt}{\input{myfile.txt}}{Does not exist!}

\section{IfFileChangedTF} \IfFileChangedTF{myfile.txt}{\input{myfile.txt}}{Didn't change!}

\IfFileChangedTF{myfile.txt}{\input{myfile.txt}}{Didn't change!} \end{document}

  • But if I replace 'This is my file.' to 'This is my file. Change.' I get furthermore 'Didn't change.'? – cis Aug 06 '20 at 13:03
  • @cis If you compile twice, yes. The conditional only returns <true> once immediately after the file changes, and when it does the MD5 is updated and the previous state is “forgotten”. This is the problem with functions with side-effect, but I don't see a much better option (if you do, please suggest). – Phelype Oleinik Aug 06 '20 at 13:10
  • Mhh, whatever I add ("This is my file. xxx") and no matter how often I compile 1 time, 2 times, ... I always get 'Didn't Change.' – cis Aug 06 '20 at 13:13
  • @cis That's odd. Please try this document and send me the .log to take a look. – Phelype Oleinik Aug 06 '20 at 13:28
  • Ehhh, ok: https://pastebin.com/mVB1YXbx – cis Aug 06 '20 at 13:33
  • @cis That's really odd... The file I gave you makes an always-changing document, so it must return <true>. Could you please run the same file, but with \tracingall right before the \IfFileChangedTF, then send me the .log again? – Phelype Oleinik Aug 06 '20 at 13:39
  • Wow, nice, thank you! This is a nice tutorial on expl3, BTW. So I understand that the makeatletter thing is to avoid ExplSynataxOn on the aux file, and that you need to use token list instead of string for property lists? It seems that strings and tl are sort-of equivalent, for example when reading the hash? – Rmano Aug 06 '20 at 16:48
  • @Rmano Yes, I preferred to use LaTeX2e-style c@mm@nds in the aux here because they will be written at arbitrary points in the document, so there may be a lot of \ExplSyntaxOn/Off, but usually there's no harm in using expl3 there. About property lists, no, they store anything in them, it doesn't have to be a string (I should probably have used strings in my answer, as the output of \file_get_mdfive_hash:nN is more of a string than a token list...) – Phelype Oleinik Aug 06 '20 at 16:58
  • Another small comment: so this will check if the file has changed since the last time you checked, correct? Not since the last time you compiled the source. – Rmano Aug 06 '20 at 18:06
  • @Rmano That's right, because when you check and the MD5 differ, it is automatically updated. You could implement otherwise by having two property lists: one for the MD5 of the previous run, which you'd use to check the file, and another for the current run, which you'd use to store the new MD5 sums, and save at the end. – Phelype Oleinik Aug 06 '20 at 19:09
1

I can offer an environment DifferentFileContents which has the same syntax as the filecontents*-environment.

The content of the DifferentFileContents-environment will be compared to the content of the file specified.

In case the contents differ or the file specified does not exist, the file will be destroyed and rewritten/will be created anew with the content of the environment.

This may be useful for reducing the amount of write-operations on Solid-State-Drives.

Internally the filecontents*-environment is used for this.

An optional argument, if present with the call to the DifferentFileContents-environment, is handed over to the filecontents*-environment.

If the filecontents*-environment (LaTeX 2ε-release v1.3c, 2019/09/11 and newer) does process optional arguments, things should be alright.

If the filecontents*-environment (LaTeX 2ε-release older than v1.3c, 2019/09/11) does not process optional arguments, the opening square bracket [ of the optional argument will be taken for the name of the file to create and an attempt at creating such a file will take place. The remainder of the optional argument and the non-optional filename-argument will cause all kinds of error messages.

This means:

If you use a LaTeX 2ε-release where the filecontents*-environment processes an optional argument (LaTeX 2ε-release v1.3c, 2019/09/11 and newer), then the DifferentFileContents-environment can also process an optional argument.

If you use a LaTeX 2ε-release where the filecontents*-environment does not processes an optional argument (LaTeX 2ε-release older than v1.3c, 2019/09/11), then the DifferentFileContents-environment also cannot process an optional argument.


The example below creates a file myfile.txt.

Hereby an already existing file myfile.txt may get destroyed/overridden.

After compiling the log-file and the terminal will contain messages informing the user whether content of environment and content of file were the same and therefore the file was not created anew / whether content of environment and content of file were not the same and therefore the file was created anew.

\documentclass{article}
\usepackage{filecontents}

\makeatletter \newcommand\PassFirstToSecond[2]{#2{#1}}% \newcommand\Exchange[2]{#2#1}% %%----------------------------------------------------------------------------- %% Stringify the first token of the second argument: %%............................................................................. \newcommand\UD@StringifySecond[2]{% \expandafter\PassFirstToSecond\expandafter{\string#2}{#1}%% }% %%----------------------------------------------------------------------------- %% Check whether argument is empty: %%............................................................................. %% \UD@CheckWhetherNull{<Argument which is to be checked>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is empty>}% %% {<Tokens to be delivered in case that argument %% which is to be checked is not empty>}% %% %% The gist of this macro comes from Robert R. Schneck's \ifempty-macro: %% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J> \newcommand\UD@CheckWhetherNull[1]{% \romannumeral0\expandafter@secondoftwo\string{\expandafter @secondoftwo\expandafter{\expandafter{\string#1}\expandafter @secondoftwo\string}\expandafter@firstoftwo\expandafter{\expandafter @secondoftwo\string}@firstoftwo\expandafter{} @secondoftwo}% {@firstoftwo\expandafter{} @firstoftwo}% }% %%----------------------------------------------------------------------------- %% Extract first inner undelimited argument: %% %% \romannumeral0\UD@ExtractFirstArgLoop{ABCDE\UD@SelDOm} yields {A} %% %% \romannumeral0\UD@ExtractFirstArgLoop{{AB}CDE\UD@SelDOm} yields {AB} %%............................................................................. @ifdefinable\UD@RemoveTillUD@SelDOm{% \long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}% }% \newcommand\UD@ExtractFirstArgLoop[1]{% \expandafter\UD@CheckWhetherNull\expandafter{@firstoftwo{}#1}% { #1}% {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}% }% %%----------------------------------------------------------------------------- \newcommand\DifferentFilecontentsVerbatimcatcodes{% \let\do=@makeother \dospecials \catcode\endlinechar=12 % \catcode\^^I=12 % }% \newcommand\DifferentFilecontents{% \begingroup \@ifnextchar[\DifferentFilecontents@opt\DifferentFilecontents@noopt }% \newcommand\DifferentFilecontents@opt[2][]{% \DifferentFilecontentsVerbatimcatcodes \expandafter\@DifferentFilecontents\expandafter{\the\inputlineno}{[{#1}]}{#2}% }% \newcommand\DifferentFilecontents@noopt[1]{% \DifferentFilecontentsVerbatimcatcodes \expandafter\@DifferentFilecontents\expandafter{\the\inputlineno}{}{#1}% }% \newcommand\DifferentFilecontentsEqualMessage[2]{% \GenericWarning{% \space\space\space\@spaces\@spaces\@spaces\@spaces }{% LaTeX Information: % Seems the content of the existing file#1'\MessageBreak equals the content of environment DifferentFilecontents\MessageBreak% on input lines #2 - \the\inputlineno.\MessageBreak The file will not be overwritten/will not be created anew@gobble }% }% \newcommand\DifferentFilecontentsDifferentMessage[2]{% \GenericWarning{% \space\space\space@spaces@spaces@spaces@spaces }{% LaTeX Information: % Seems either the file `#1' does not exist\MessageBreak or its content does not equal the content of environment\MessageBreak DifferentFilecontents on input lines #2 - \the\inputlineno.\MessageBreak The file will be overwritten/created anew@gobble% }% }%

\newread\filecompareread

\begingroup \catcode\endlinechar=12\relax% \edef\delimitersbehind{@backslashchar end\string{DifferentFilecontents\string}}% \edef\filecontentsbegin{@backslashchar begin\string{filecontents\string}}% \edef\filecontentsend{@backslashchar end\string{filecontents\string}}% \newcommand@DifferentFilecontents[4]{% \endgroup% @ifdefinable@DifferentFilecontents{% % ##1 - input-line-number of begin of environment % ##2 - optional arguments % ##3 - file name % ##4 - environment body % #1 = \end{readenvironmentbody} % #2 = ^^M % #3 = \begin{filecontents} % #4 = \end{filecontents}% \long\def@DifferentFilecontents##1##2##3##4#2#1{% \immediate\openin\filecompareread=##3\relax% %\message{Environment-Body: (\detokenize{|##4|})}% \UD@CheckWhetherNull{##4}{% \DifferentFilecontentsCompareLoop{^^M}% }{% \expandafter\DifferentFilecontentsCompareLoop\expandafter{@gobble##4^^M^^M}% }% {##2}{##3}{##4}{}{##1}% {% \immediate\closein\filecompareread\relax% \endgroup% }% \end{DifferentFilecontents}% }% }% \newcommand\DifferentFilecontentsCompareLoop[6]{% % ##1 - remainder of environment body % ##2 - optional arguments % ##3 - file name % ##4 - entire environment body % ##5 - remainder of current line of file % ##6 - input-line-number of begin of environment % #1 = \end{readenvironmentbody} % #2 = ^^M % #3 = \begin{filecontents} % #4 = \end{filecontents}% \UD@CheckWhetherNull{##5}{% %\message{1}% \ifeof\filecompareread\expandafter@firstoftwo\else\expandafter@secondoftwo\fi% {% %\message{1-1}% \UD@CheckWhetherNull{##1}{% %\message{1-1-1}% \Exchange{% \DifferentFilecontentsEqualMessage{##3}{##6}% }% }{% %\message{1-1-2}% \UD@CheckWhetherNull{##4}{% %\message{1-1-2-1}% %\message{Writing:(#3{##3}#2#4)}% \Exchange{% \DifferentFilecontentsDifferentMessage{##3}{##6}% \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#3##2% {##3}#2#4}% }% }{% %\message{1-1-2-2}% %\message{Writing:(#3{##3}##4#2#4)}% \Exchange{% \DifferentFilecontentsDifferentMessage{##3}{##6}% \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#3##2% {##3}##4#2#4}% }% }% }% }{% %\message{1-2}% \immediate\read\filecompareread to\templine% \expandafter\PassFirstToSecond\expandafter{\templine}{% \DifferentFilecontentsCompareLoop{##1}{##2}{##3}{##4}% }{##6}% }% }{% %\message{2}% \UD@CheckWhetherNull{##1}{% %\message{2-1}% \UD@CheckWhetherNull{##4}{% %\message{2-1-1}% %\message{Writing:(#3{##3}#2#4)}% \Exchange{% \DifferentFilecontentsDifferentMessage{##3}{##6}% \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#3##2% {##3}#2#4}% }% }{% %\message{2-1-2}% %\message{Writing:(#3{##3}##4#2#4)}% \Exchange{% \DifferentFilecontentsDifferentMessage{##3}{##6}% \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#3##2% {##3}##4#2#4}% }% }% }{% %\message{2-2}% %\edef\tempa{\romannumeral0\UD@ExtractFirstArgLoop{##5\UD@SelDOm}}% %\message{\detokenize\expandafter{\tempa}}% %\edef\tempb{\romannumeral0\UD@ExtractFirstArgLoop{##1\UD@SelDOm}}% %\message{\detokenize\expandafter{\tempb}}% \expandafter\UD@StringifySecond\expandafter\Exchange% \romannumeral0\UD@ExtractFirstArgLoop{##5\UD@SelDOm}{% \expandafter\UD@StringifySecond\expandafter\Exchange% \romannumeral0\UD@ExtractFirstArgLoop{##1\UD@SelDOm}{\ifx}% }% \expandafter@firstoftwo\else\expandafter@secondoftwo\fi% {% %\message{2-2-1}% \expandafter\PassFirstToSecond\expandafter{@gobble##5}{% \expandafter\DifferentFilecontentsCompareLoop\expandafter{@gobble##1}{##2}{##3}{##4}% }{##6}% }{% %\message{2-2-2}% \UD@CheckWhetherNull{##4}{% %\message{2-2-2-1}% %\message{Writing:(#3{##3}#2#4)}% \Exchange{% \DifferentFilecontentsDifferentMessage{##3}{##6}% \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#3##2% {##3}#2#4}% }% }{% %\message{2-2-2-2}% %\message{Writing:(#3{##3}##4#2#4)}% \Exchange{% \DifferentFilecontentsDifferentMessage{##3}{##6}% \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#3##2% {##3}##4#2#4}% }% }% }% }% }% }% }% \expandafter\PassFirstToSecond\expandafter{\filecontentsend}{% \expandafter\PassFirstToSecond\expandafter{\filecontentsbegin}{% \expandafter\PassFirstToSecond\expandafter{\delimitersbehind}{% @DifferentFilecontents% }{^^M}% }% }%

\makeatother

\begin{document}

%--------------------------------------------------------------------

\begin{DifferentFilecontents}%[overwrite]% {myfile.txt} This is my file.

This is my file. \end{DifferentFilecontents}

\section{The first variant of myfile.txt} \IfFileExists{myfile.txt}{\input{myfile.txt}}{Does not exist!}

%--------------------------------------------------------------------

\begin{DifferentFilecontents}%[overwrite]% {myfile.txt} This is my file.

This is my file. \end{DifferentFilecontents}

\section{Once more the first variant of myfile.txt} \IfFileExists{myfile.txt}{\input{myfile.txt}}{Does not exist!}

%--------------------------------------------------------------------

\begin{DifferentFilecontents}%[overwrite]% {myfile.txt} This is my file - second variant.

This is my file - second variant. \end{DifferentFilecontents}

\section{myfile.txt -- the second variant} \IfFileExists{myfile.txt}{\input{myfile.txt}}{Does not exist!}

\end{document}

The console-output:

$ pdflatex test.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2019/dev/Debian) (preloaded format=pdflatex).
entering extended mode
(./test.tex
LaTeX2e <2018-12-01>
(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
Document Class: article 2018/09/03 v1.4i Standard LaTeX document class
(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texlive/texmf-dist/tex/latex/filecontents/filecontents.sty)
(./test.aux)

LaTeX Information: Seems either the file `myfile.txt' does not exist or its content does not equal the content of environment DifferentFilecontents on input lines 250 - 254. The file will be overwritten/created anew.

LaTeX Warning: Overwriting file `./myfile.txt'.

(./myfile.txt)

LaTeX Information: Seems the content of the existing file `myfile.txt' equals the content of environment DifferentFilecontents on input lines 262 - 266. The file will not be overwritten/will not be created anew.

(./myfile.txt)

LaTeX Information: Seems either the file `myfile.txt' does not exist or its content does not equal the content of environment DifferentFilecontents on input lines 274 - 278. The file will be overwritten/created anew.

LaTeX Warning: Overwriting file `./myfile.txt'.

(./myfile.txt) [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux) )</usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmb x12.pfb></usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb > Output written on test.pdf (1 page, 25863 bytes). Transcript written on test.log.

Image of the resulting .pdf-file:

enter image description here

As you can see on the console-output, LaTeX 2ε-release 2018-12-01, which is older than LaTeX 2ε-release v1.3c, 2019/09/11, was used for compiling the example. The filecontents*-environment of this older release does not process optional arguments. Thus in the example optional arguments are commented out and the package filecontents is loaded for turning the filecontents*-environment into something that can not only be used in the preamble but can be used within the document-environment also and that also does overwrite existing files.

Ulrich Diez
  • 28,770