Use \immediate\write18 with Single Quote

bashshell-escapewrite

I would like to use \immediate\write18 (encoded in the \OuputToFileA macro) to output a string containing a single quote. If I replace the single quote with another character, such as X

\StrSubstitute{\@CurrentString}{'}%
    {X}% <---- Replace the single quote
    [\@CurrentString]%

things work fine. But, I need the single quote in the output. Anything else I tried, such as escaping the single quote:

\StrSubstitute{\@CurrentString}{'}%
    {\@backslashchar\@backslashchar'}% <---- Attempt to escape the single quote
    [\@CurrentString]%

all result in an empty file.

Using \immediate\write (see \OuputToFileB macro) instead seems to work fine. How do I get this to work with \immediate\write18 instead?

Desired Output:

Afer pdflatex, the file foo.tex should contain

enter image description here

Notes:

  • Do not run this if you have foo.tex in the current directory as that will be overwritten.

Code:

\documentclass{article}
\usepackage{xparse}
\usepackage{xstring}

\immediate\write18{printf "\\n" > foo.tex }% Initialize File

\makeatletter
\NewDocumentCommand{\OuputToFileA}{%
    m% string to output
}{%
    Output A to pdf: #1
    \def\@CurrentString{#1}% 
    %% Using the first \StrSubstitute works. But, I don't want to replace the single quote
    %\StrSubstitute{\@CurrentString}{'}%
    %    {X}% <---- Replace the single quote
    %    [\@CurrentString]%
    \StrSubstitute{\@CurrentString}{'}%
        {\@backslashchar\@backslashchar'}% <---- Attempt to escape the single quote
        [\@CurrentString]%
    %% ---------------------------------
    \immediate\write18{printf 'string = "\@CurrentString"' >> foo.tex }%
    \immediate\write18{printf "\\n" >> foo.tex }%
}
\NewDocumentCommand{\OuputToFileB}{%
    m% string to output
}{%
    Output B to pdf: #1
    \def\@CurrentString{#1}% No escaping required with \immediate
    %% ---------------------------------
    \newwrite\MyFile
    \immediate\openout\MyFile=foo.tex
    \immediate\write\MyFile{\@CurrentString}%
    \immediate\closeout\MyFile%
}
\makeatother

\begin{document}
    \OuputToFileA{\detokenize{Einstein's Formula $E = mc^2$}}%
    %\OuputToFileB{\detokenize{Einstein's Formula $E = mc^2$}}% <-- Works!
\end{document}

Best Answer

I've found a nice trick at https://stackoverflow.com/a/1250279/923955

Replace ' with '"'"'

In my opinion, the expl3 functions are much better than those of xstring. Besides, \sys_shell_now:n does no expansion to the input, without any other need. Alternating with \sys_shell_now:x is handier.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
% Initialize File
\sys_shell_now:n {printf~"\\n"~>~quotegrill.txt}

\NewDocumentCommand{\OuputToFile}{m}
  {% #1 is the string to write
    Output ~ to ~ pdf: ~ #1
    \str_set:Nn \l_tmpa_str { #1 }
    \str_replace_all:Nnn \l_tmpa_str { ' } { '"'"' }
    \sys_shell_now:x {printf~'string~=~"\l_tmpa_str"'~>>~quotegrill.txt}
    \sys_shell_now:n {printf~"\\n"~>>~quotegrill.txt}
}
\ExplSyntaxOff

\begin{document}

\OuputToFile{Einstein's Formula $E = mc^2$}

\end{document}

The output file will be


string = "Einstein's Formula $E = mc^2$"
Related Question