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?

5 Answers 5


You can try


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

\def\setexeccatcodes{\catcode`\#=12 \catcode`\%=12 }

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:


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
    Commented Mar 25, 2011 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
    Commented Mar 25, 2011 at 11:12
  • Using \write18{\detokenize{...}} would also be a possibility, wouldn't it? Commented Jul 28, 2011 at 0:16
  • @Martin I guess so.
    – egreg
    Commented Jul 28, 2011 at 8:10
  • 1
    @ガベージコレクタ Just add \catcode`$=12
    – egreg
    Commented Oct 7, 2012 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


(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). Commented Mar 25, 2011 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
    Commented Mar 25, 2011 at 6:58
  • @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). Commented Mar 25, 2011 at 14:29

With xparse's verbatim argument type introduced a couple of months ago, this is quite easy.


  { \immediate \write 18 { \tl_to_str:n {#1} } }

\commandline|echo "Hello World! @#$%^"|


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.



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

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

Try using the bashful package:

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

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .