# How can I provide a verbatim (unescaped) commandline for executing with \write18?

When using \write18 to execute shell commands, there are some cases where lots of backslash strings, and other 'special' LaTeX characters are required in the commandline to be executed (e.g. if a regular-expression is needed, or when using awk).

It would be convenient to have a way of typing the command exactly as it should appear when used on the commandline (i.e. without needing to use \string, etc.), and having it parsed verbatim, and given as an argument to \write18. What ways are there of accomplishing this?

-

You can try

\write18{\unexpanded{...}}


In order to avoid doubled # marks and to use %, you can define

\def\exec{\begingroup\setexeccatcodes\innerexec}
\def\setexeccatcodes{\catcode\#=12 \catcode\%=12 }
\def\innerexec#1{\immediate\write18{\unexpanded{#1}}\endgroup}


However you can't use \exec in the argument of another command, because this freezes category codes. Should you need it, one has to work also with \scantokens. Braces must be balanced, but this should not be a problem.

Another possibility, suggested by Martin Scharrer, is to use \detokenize:

\def\exec#1{\immediate\write18{\detokenize{#1}}}


which however might insert unwanted spaces in case backslashes are used in the argument. The advantage is that this macro can be used in arguments to other commands.

-
Nice -- I wasn't aware of \unexpanded. By the way, the # -> ## problem (mentioned in Hendrik's comment below) still exists with \unexpanded. I guess one could write a \execunexpanded macro which handles this? –  Mark Mar 25 '11 at 11:01
One other thing \unexpanded seems not to handle well is %. I guess one would need to do something similar with catcodes? –  Mark Mar 25 '11 at 11:12
Using \write18{\detokenize{...}} would also be a possibility, wouldn't it? –  Martin Scharrer Jul 28 '11 at 0:16
@Martin I guess so. –  egreg Jul 28 '11 at 8:10
@ガベージコレクタ Just add \catcode$=12 – egreg Oct 7 '12 at 10:48 egreg has an answered for the typical case where e-TeX is available. In the original TeX-82 program you'd do effectively the same using a token register \newtoks\mytoks \mytoks{<commands-go-here>} \immediate\write18{\the\mytoks}  (This works because token registers are only expanded once in \write contexts, in analogy to their behaviour inside \edef. The \unexpanded solution effectively does the same thing without needing the assignment operation.) - This has the drawback that a # would be doubled (which has already confused me a lot). – Hendrik Vogt Mar 25 '11 at 6:34 @Hendrik: If you need to echo # in this context I'd change the catcode temporarily. (Getting single # tokens is a pain, as you know.) – Joseph Wright Mar 25 '11 at 6:58 Ah, that's a possibility, thanks. – Hendrik Vogt Mar 25 '11 at 7:02 @Hendrik: a crazier alternative to changing the catcode of # locally is to globally do \let\macparchar#\catcode\#=13\let#\macparchar. Then #, albeit active, will work as usual, and to disable its doubling you can just \let#\relax (then that works in the argument of a command). – Bruno Le Floch Mar 25 '11 at 14:29 With xparse's verbatim argument type introduced a couple of months ago, this is quite easy. \documentclass{article} \usepackage{xparse} \ExplSyntaxOn \NewDocumentCommand{\commandline}{v} { \immediate \write 18 { \tl_to_str:n {#1} } } \ExplSyntaxOff \commandline|echo "Hello World! @#$%^"|

\stop

-

The 2011/07/23 version of my newverbs package now provides \Verbdef which can be used to define verbatim material into macros using a \write-able form (I call it plain verbatim). The verbdef package provides \verbdef which defines macros with additional font related code to typeset the material which makes it unsuitable for \write.

\usepackage{newverbs}[2011/07/23]

\Verbdef\myhash{#}
\Verbdef\mypercent{%}

\immediate\write18{foo \myhash bar \mypercent bar }

\Verbdef\mycommand|perl dosomething.sh '4%^*&_{#'|
\immediate\write18{\mycommand}

-

Try using the basfhul package:

\bash
echo "\documentclass{Article}\begin{document}Hello, World!\end{document}" > try.tex
latex try.tex
\END


should work as expected, i.e., generate a latex file from within latex, and then submitting it to processing by latex.

-