6

When writing things to the aux file that should be expanded at the next runs \begin{document} call, expansion of those things must be canceled out. This can be done by preceding the active character (\) with

  1. \string
  2. \protect

Therefore, when writing the macro \saveForLater to the aux file, I could do:

\immediate\write\@mainaux{\string\gdef\string\saveForLater{123}}

But when I want to write that same macro (control sequence) to the aux file using the \csname syntax, I have to use a \noexpand

\immediate\write\@mainaux{\string\expandafter\gdef\noexpand\csname saveForLater@\somedef\string\endcsname{456}}

\string\csname does not work, yet \string works fine (and is required) on the \endcsname.

Please explain this.

Example

\documentclass{article}
\makeatletter
\def\saveForLater{}%init
\def\somedef{HEY}
\begin{document}
\immediate\write\@mainaux{\string\gdef\string\saveForLater{123}}
\immediate\write\@mainaux{\string\expandafter\gdef\string\csname saveForLater@\somedef\string\endcsname{456}} % DOES NOT WORK
\immediate\write\@mainaux{\string\expandafter\gdef\noexpand\csname saveForLater@\somedef\string\endcsname{456}} % WORKS
\end{document}

Uncomment the Line and Behold

! Undefined control sequence.
l.3 \expandafter\gdef \csnamesaveForLater@HEY
                                         \endcsname{456}

Aux File with Everything Uncommented

(and forcefully exiting due to error)

\relax
\gdef\saveForLater{123}
\expandafter\gdef \csnamesaveForLater@HEY\endcsname{456}
\expandafter\gdef \csname saveForLater@HEY\endcsname{456}
  • Doesn't \gdef\expandafter\string\csname saveForLater\endcsname work? – Manuel Nov 14 '17 at 14:09
7

Recall that \write does complete expansion until unexpandable tokens remain (when the write operation actually takes place). Since \gdef is unexpandable, it needs not be preceded by \string, unless you want to avoid a space following it.

There is a timing problem with your code (let me omit \immediate that's irrelevant here):

\write\@mainaux{\string\expandafter\gdef\noexpand\csname saveForLater@\somedef\string\endcsname{456}}
  1. the token \string is expandable, so it acts on the following token (\expandafter)
  2. \gdef is not expandable
  3. \noexpand is expandable, so it acts on the next token (\csname)
  4. the characters in saveForLater@ are unexpandable
  5. \somedef should expand to character tokens, let's say it expands to foo
  6. \string is expandable, so it acts on the next token (\endcsname)
  7. the characters in {456} are unexpandable.

Thus what you get written is

\expandafter\gdef \csname saveForLater@foo\endcsname{456}

With \string\csname you'd get

\expandafter\gdef \csnamesaveForLater@foo\endcsname{456}

because there is no space following \csname in the token list passed to \write.

Why is there one with \noexpand? For the same reason there is one after \gdef: TeX adds a space following an unexpandable control word when it performs a write operation. With \noexpand\csname the normally expandable \csname is made temporarily equivalent to \relax, so it gets written with a space following it, just like \relax would (or \gdef).

\immediate\write\@auxout{%
  \gdef\expandafter\noexpand\csname saveForLater@\somedef\endcsname{456}%
}

will write

\gdef \saveForLater@foo {456}

because \expandafter causes the control sequence being formed by \csname before \noexpand can act on it.

If you want to preserve the \csname in the .aux file:

\immediate\write\@auxout{%
  \noexpand\expandafter
  \gdef\noexpand\csname saveForLater@\somedef\endcsname{456}%
}

that will write

\expandafter \gdef \csname saveForLater@foo\endcsname {456}
8
\immediate\write\@mainaux{\gdef \string\saveForLater{123}}

This should work since it saves \gdef\saveForLater{123} in the .aux.

\immediate\write\@mainaux{\string\expandafter\gdef\string\csname saveForLater\string\endcsname{456}}

The problem there is that you are saving \expandafter\gdef \csnamesaveForLater\endcsname{456} in the .aux, because \string doesn't add a space after outputting the control sequence name. So when you read again and see \csnamesaveForLater TeX doesn't know what that is. You would need \string\csname\space. But I don't know what you intend, simpler seems

\immediate\write\@mainaux{\gdef\expandafter\string\csname saveForLater\endcsname{456}}

which saves \gdef\saveForLater{456}. If that's not what you intend, you need that \space there.

Or, as you've already seen, use \noexpand.

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.