15

When I write documents that contain source code examples and snippets the listings package is a great solution to include such things. Especially with the \lstinputlisting command and firstline/lastline options you don't have to do copy/pasting and you are able to easily test your examples.

But what about the integration of command line sessions? For example a bash session, a session in the Haskell interpreter hugs or in the Python shell?

Ideally I would like to be able to write something like this in LaTeX:

\begin{hugs}[nostartbanner]
take 3 [1..]
reverse "hello world"
\end{hugs}

And get following output in my document:

Hugs> take 3 [1..]
[1,2,3]
Hugs> reverse "hello world"
"dlrow olleh"

(perhaps plus some syntax highlighting)

That means such an environment should automagically start an external shell (e.g. hugs) and should paste the session output in the document. And for bonus points it should do some caching of the output.

Such an approach would help to reduce typos in anticipated output and copy-n-pasting orgies (which are of course error prone).

Also while writing documentation for a comment line program or a shell that is still in rapid development you don't have to manually check all your existing examples when the output is automatically integrated in the LaTeX document.

How can I solve this with LaTeX?

I found Listing language for “screen session”, but this question is just about the syntax highlighting part.

1
  • 3
    The package bashful should do what you're looking for.
    – egreg
    Commented Oct 14, 2011 at 9:31

2 Answers 2

11

As @egreg recommended, the bashful package is specifically designed for this. Quoting from the user manual:

...bashful provides a convenient interface to TEX’s primitive \write18—the execution of shell com- mands from within your input files, also known as shell escape. Text between \bash and \END is executed by bash, a popular Unix command line interpreter. Various flags control whether the executed commands and their output show up in the printed document, and whether they are saved to files.

Although provisions are made for using shells other than bash, this package may not operate without modifications on Microsoft’s operating systems.

Note that this requires -shell-escape option to be specified.

Here I have used only basic bash commands. I suspect that you could do something similar with the Haskell interpreter.

enter image description here

The first line of the bash script file begins with a %, so I started the listings form line 2, and left the first line blank. When you initiate the \bash command, you provide a file name for the .sh bash file and the output file. Then use lstinputlisting to reincorporate the contents of that file back into the .tex file.

\documentclass{standalone}
\usepackage{xcolor}
\usepackage{bashful}
\usepackage{listings}

\lstdefinestyle{BashInputStyle}{
  language=bash,
  firstline=2,% Supress the first line that begins with `%`
  basicstyle=\small\sffamily,
  numbers=left,
  numberstyle=\tiny,
  numbersep=3pt,
  frame=tb,
  columns=fullflexible,
  backgroundcolor=\color{yellow!20},
  linewidth=0.9\linewidth,
  xleftmargin=0.1\linewidth
}

\lstdefinestyle{BashOutputStyle}{
  basicstyle=\small\ttfamily,
  numbers=none,
  frame=tblr,
  columns=fullflexible,
  backgroundcolor=\color{blue!10},
  linewidth=0.9\linewidth,
  xleftmargin=0.1\linewidth
}


\begin{document}
\bash[verbose,scriptFile=hello.sh,stdoutFile=hello.tex]

echo "Hello World!"
echo "Today is" `date`
echo ""
echo "Disk usage is:"
df
\END
\par\noindent
Executing the following code in \textbf{bash}
\lstinputlisting[style=BashInputStyle]{hello.sh}
%
yields the following output:
\lstinputlisting[style=BashOutputStyle]{hello.tex}
\end{document}
3

In ConTeXt, the filter module allows you to run any external program and read its input back. The basic setup is (I am using ghci rather than hugs because I don't have hugs installed).

\usemodule[filter]

\defineexternalfilter
  [ghci]
  [
    filtercommand={cat \externalfilterinputfile\space | ghci > \externalfilteroutputfile},
    readcommand=\typefile,
    output=\externalfilterbasefile.out,
  ]

After this, the contents of \startghci ... \stopghci will be passed to ghci and its output typed back into ConTeXt. You can further configure this using

  • Change readcommand to \typefile[range={6,-2}]. This will only typeset lines from 6 up to the second last line of the output file. For Hugs, this can be used to remove the banner.

  • Add continue=yes. With this option, the results will be cached and rerun only if the md5sum of the file has changed. But continue=yes does not work with commands that contain a |. You can either create a .sh or .cmd file that runs the redirection or, if ghci provides a command line argument to specify the input file, use that.

  • Add directory=... to redirect the temporary files to a different directory.

  • Use before and after keys to add something before or after the environment. This can be useful if you want to typeset the syntax highlighted input file.

Below is a complete example: \usemodulefilter \usemodule[vim]

\definevimtyping[haskell][syntax=haskell]


\defineexternalfilter
  [ghci]
  [
    filtercommand={cat \externalfilterinputfile\space | ghci > \externalfilteroutputfile},
    readcommand={\typefile[range={6,-2}]},
    output=\externalfilterbasefile.out,
    before={\typehaskellfile{\externalfilterinputfile}},
  ]

  \starttext
  \startTEXpage
  \startghci
    take 3 [1..]
    reverse "hello world"
  \stopghci
  \stopTEXpage
  \stoptext

which gives

enter image description here

Notice that the output is not exactly same as what you wanted, because ghci does not echo the input line. I don't know if there is a command line option to change that.

You must log in to answer this question.

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