4

If I have a document whose name is my exam.tex, is it possible to make its output PDF file have a name of my exam [solved].pdf by appending the word [solved] only when the answers class option is enabled?

\documentclass[answers]{exam}
\begin{document}
    text
\end{document}
Diaa
  • 9,599
  • no, you can't change \jobname then. You would have to restart compilation (this needs --shell-escape). – Ulrike Fischer Dec 24 '19 at 21:20
  • @UlrikeFischer I am sorry, but I don't quite understand; do you mean that to make this work I have to compile twice with --shell-escape? If yes, I would be grateful if you could provide an answer to show me how to do so. – Diaa Dec 24 '19 at 21:24
  • You could search for the previous \jobname.pdf and copy it to another filename. That way they will have different names, even though you can't change the current one. Frankly, it would be easier just to rename the source file. – John Kormylo Dec 24 '19 at 21:41
  • I think @UlrikeFischer means that you could start a new compilation process if answers is given as an option. That compilation process could output whatever filename you want, but you can't change the output filename of the current compilation process. (But you could probably run the new process and then end the current one without outputting anything, so you'd only end up with the other-named PDF. Your log file and all the rest would get the current filename, though, and you'd have to run any additional things you need for the new filename (e.g. bibtex/biber/makeindex/another run etc.). – cfr Dec 25 '19 at 03:33
  • You can also write a small (shell) script around the compilation that reads the first line of the file and calls LaTeX with the correct jobname depending on whether the first line contains the word answers or not. On Linux/Mac you can use grep for this, in Windows findstr. – Marijn Dec 25 '19 at 17:35

1 Answers1

3

Here's my solution to this problem.

If you compile without -shell-escape, it just runs as normal (i.e., if you are using the answers option it will output answers, otherwise not; the default \jobname is used.)

If you compile with -shell-escape, then it outputs two files: \jobname.pdf with questions and \jobname-solved.pdf with answers. It does this regardless of whether the answers option is passed to the class or not.

I've used latexmk so that required multiple compilations are taken into account automatically.

MWE

\documentclass{exam}

\usepackage{pdftexcmds}
\usepackage{iftex}

\makeatletter

\ltx@IfUndefined{pdf@shellescape}
  {}
  {\ifnum\pdf@shellescape=1
     \ifpdftex
       \def\latexmkengine{-pdf}%
     \fi
     \ifluatex
       \def\latexmkengine{-lualatex}%
     \fi
     \ifxetex
       \def\latexmkengine{-xelatex}%
     \fi
     \pdf@system{%
       latexmk \latexmkengine\space -jobname="\jobname-solved"
         -usepretex="\string\AtBeginDocument{\string\printanswerstrue}"
         "\jobname"
     }%
     \pdf@system{%
       latexmk \latexmkengine\space
         -usepretex="\string\AtBeginDocument{\string\printanswersfalse}"
         "\jobname"
     }%
     \expandafter\stop
   \fi}

\makeatother

\begin{document}
\begin{questions}
  \question One of these things is not like the others; one of these things is
  not the same. Which one is different?
  \begin{oneparchoices}
    \choice John
    \choice Paul
    \choice George
    \choice Ringo
    \CorrectChoice Socrates
  \end{oneparchoices}
\end{questions}
\end{document}

Output

\jobname.pdf

output

\jobname-solved.pdf

output


If you want to only output one file at a time, you can slightly modify the above answer so that latexmk is only called when the answers option is actually passed to the exam class. This answers your question as you've asked it. It still needs to be compiled with -shell-escape.

\documentclass[answers]{exam}

\usepackage{pdftexcmds}
\usepackage{iftex}

\makeatletter

\ltx@IfUndefined{pdf@shellescape}
  {}
  {\ifnum\pdf@shellescape=1
     \ifpdftex
       \def\latexmkengine{-pdf}%
     \fi
     \ifluatex
       \def\latexmkengine{-lualatex}%
     \fi
     \ifxetex
       \def\latexmkengine{-xelatex}%
     \fi
     \ifprintanswers
       \pdf@system{%
         latexmk \latexmkengine\space -jobname="\jobname-solved" "\jobname"
       }%
       \expandafter\expandafter\expandafter\stop
     \fi
   \fi}

\makeatother

\begin{document}
\begin{questions}
  \question One of these things is not like the others; one of these things is
  not the same. Which one is different?
  \begin{oneparchoices}
    \choice John
    \choice Paul
    \choice George
    \choice Ringo
    \CorrectChoice Socrates
  \end{oneparchoices}
\end{questions}
\end{document}

And an expl3 solution that handles file names with spaces. Still needs -shell-escape of course.

Ouptut two files

\documentclass{exam}

\usepackage{expl3}

\ExplSyntaxOn

\str_new:N \l__diaa_latexmk_engine_str
\str_new:N \g__diaa_solved_jobname_str
\str_const:Nn \l__diaa_latexmk { latexmk }

\cs_new:Nn \__diaa_build_solved_jobname:
  {
    \str_gset:Nx \g__diaa_solved_jobname_str { \c_sys_jobname_str }
    \str_gremove_all:Nn \g__diaa_solved_jobname_str { " }
    \str_gput_left:Nn \g__diaa_solved_jobname_str { " }
    \str_gput_right:Nn \g__diaa_solved_jobname_str { ~[solved]" }
  }

\sys_if_shell_unrestricted:T
  {
    \sys_if_engine_luatex:T
      { \str_set:Nn \l__diaa_latexmk_engine_str { -lualatex } }
    \sys_if_engine_pdftex:T
      { \str_set:Nn \l__diaa_latexmk_engine_str { -pdf } }
    \sys_if_engine_xetex:T
      { \str_set:Nn \l__diaa_latexmk_engine_str { -xelatex } }
    \__diaa_build_solved_jobname:
    \sys_shell_now:x
      {
        \l__diaa_latexmk \c_space_tl
        \l__diaa_latexmk_engine_str \c_space_tl
        -usepretex="\string\AtBeginDocument{\string\printanswerstrue}" \c_space_tl
        -jobname=\g__diaa_solved_jobname_str \c_space_tl
        \c_sys_jobname_str
      }
    \sys_shell_now:x
      {
        \l__diaa_latexmk \c_space_tl
        \l__diaa_latexmk_engine_str \c_space_tl
        -usepretex="\string\AtBeginDocument{\string\printanswersfalse}" \c_space_tl
        \c_sys_jobname_str
      }
    \stop
  }

\ExplSyntaxOff

\begin{document}
\begin{questions}
  \question One of these things is not like the others; one of these things is
  not the same. Which one is different?
  \begin{oneparchoices}
    \choice John
    \choice Paul
    \choice George
    \choice Ringo
    \CorrectChoice Socrates
  \end{oneparchoices}
\end{questions}
\end{document}

Output one file

\documentclass[answers]{exam}

\usepackage{expl3}

\ExplSyntaxOn

\str_new:N \l__diaa_latexmk_engine_str
\str_new:N \g__diaa_solved_jobname_str
\str_const:Nn \l__diaa_latexmk { latexmk }

\cs_new:Nn \__diaa_build_solved_jobname:
  {
    \str_gset:Nx \g__diaa_solved_jobname_str { \c_sys_jobname_str }
    \str_gremove_all:Nn \g__diaa_solved_jobname_str { " }
    \str_gput_left:Nn \g__diaa_solved_jobname_str { " }
    \str_gput_right:Nn \g__diaa_solved_jobname_str { ~[solved]" }
  }

\sys_if_shell_unrestricted:T
  {
    \sys_if_engine_luatex:T
      { \str_set:Nn \l__diaa_latexmk_engine_str { -lualatex } }
    \sys_if_engine_pdftex:T
      { \str_set:Nn \l__diaa_latexmk_engine_str { -pdf } }
    \sys_if_engine_xetex:T
      { \str_set:Nn \l__diaa_latexmk_engine_str { -xelatex } }
    \legacy_if:nT { printanswers }
      {
        \__diaa_build_solved_jobname:
        \sys_shell_now:x
          {
            \l__diaa_latexmk \c_space_tl
            \l__diaa_latexmk_engine_str \c_space_tl
            -jobname=\g__diaa_solved_jobname_str \c_space_tl
            \c_sys_jobname_str
          }
        \stop
      }
  }

\ExplSyntaxOff

\begin{document}
\begin{questions}
  \question One of these things is not like the others; one of these things is
  not the same. Which one is different?
  \begin{oneparchoices}
    \choice John
    \choice Paul
    \choice George
    \choice Ringo
    \CorrectChoice Socrates
  \end{oneparchoices}
\end{questions}
\end{document}

Output two files with a call directly to lualatex instead of latexmk:

\documentclass{exam}

\usepackage{expl3}

\ExplSyntaxOn

\str_new:N \g__diaa_solved_jobname_str
\str_const:Nn \l__diaa_latex_cmd { lualatex }
\str_const:Nn \l__diaa_latex_options { -synctex=1 ~ -interaction=nonstopmode }

\cs_new:Nn \__diaa_build_solved_jobname:
  {
    \str_gset:Nx \g__diaa_solved_jobname_str { \c_sys_jobname_str }
    \str_gremove_all:Nn \g__diaa_solved_jobname_str { " }
    \str_gput_left:Nn \g__diaa_solved_jobname_str { " }
    \str_gput_right:Nn \g__diaa_solved_jobname_str { ~[solved]" }
  }

\sys_if_shell_unrestricted:T
  {
    \__diaa_build_solved_jobname:
    \sys_shell_now:x
      {
        \l__diaa_latex_cmd \c_space_tl
        -jobname=\g__diaa_solved_jobname_str \c_space_tl
        \l__diaa_latex_options \c_space_tl
        "\string\AtBeginDocument{\string\printanswerstrue}" \c_space_tl
        "\string\input{\c_sys_jobname_str}"
      }
    \sys_shell_now:x
      {
        \l__diaa_latex_cmd \c_space_tl
        -jobname=\c_sys_jobname_str \c_space_tl
        \l__diaa_latex_options \c_space_tl
        "\string\AtBeginDocument{\string\printanswersfalse}" \c_space_tl
        "\string\input{\c_sys_jobname_str}"
      }
    \stop
  }

\ExplSyntaxOff

\begin{document}
\begin{questions}
  \question One of these things is not like the others; one of these things is
  not the same. Which one is different?
  \begin{oneparchoices}
    \choice John
    \choice Paul
    \choice George
    \choice Ringo
    \CorrectChoice Socrates
  \end{oneparchoices}
\end{questions}
\end{document}
David Purton
  • 25,884
  • Many thanks. It works as expected. However, there is one minor flaw; if the file name has spaces such as, for example, change name.tex results in this message Latexmk: Need to specify at most one filename if jobname specified without a %A, but 2 were found (after defaults and wildcarding).. Moreover, sometimes, I can have more spaces in the file name. – Diaa Dec 26 '19 at 09:45
  • Another issue, in order to get a space before [solved], I changed the respective line to -jobname="\jobname\space[solved]", did I make it the correct way? – Diaa Dec 26 '19 at 09:46
  • 1
    @Diaa, sigh. File names with spaces are stupid. It can be done, but seems a bit complicated. See https://tex.stackexchange.com/questions/93826/how-to-pass-jobname-with-whitespace-to-external-shell-command. But to be honest I wouldn't bother. – David Purton Dec 26 '19 at 10:26
  • @Diaa, I think I can put together a heavier weight example use expl3. I'll update my answer later tonight. – David Purton Dec 26 '19 at 10:43
  • @Diaa, see bottom example for code that handles file names with spaces. – David Purton Dec 26 '19 at 11:31
  • Many thanks for your consideration and so sorry for my late reply since I have been out most of the day. I would be highly grateful if you could the case of two outputs at the same time in your last code. My tex knowledge is not good enough to understand how to do it on my own. – Diaa Dec 26 '19 at 22:09
  • 1
    @Diaa, I added the expl3 option for the case producing two outputs. – David Purton Dec 27 '19 at 01:30
  • If I would like to use directly lualatex instead of running it by latexmk, what should be changed in your code? For example, I would like to execute lualatex -synctex=1 -shell-escape -interaction=nonstopmode. – Diaa Jan 21 '20 at 09:57
  • 1
    @Diaa, I added the example of calling lualatex directly. It's probably better to ask a new question in cases like this and just reference this question. Someone else might be able to provide an answer more quickly than me as only I see your comment here. – David Purton Jan 21 '20 at 12:29
  • I appreciate your consideration. I try to minimize the number of asked questions as much as possible :) – Diaa Jan 21 '20 at 13:34
  • I would be grateful if you could consider looking at my releveant question where I need to add some sort of switch to selectively compile using latexmk or lualatex. – Diaa Apr 20 '20 at 01:25