21

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?

Mark
  • 1,855

5 Answers5

14

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.

egreg
  • 1,121,712
9

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.)

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
9

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
7

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}
Martin Scharrer
  • 262,582
4

Try using the bashful 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.

Yossi Gil
  • 15,951