[Tex/LaTex] Beamer Animations: How to simulate terminal input and output

animateanimationsbeamer

I'm preparing some Unix training material and I'd like to show some vertically scrolling text of the commands as if simulating text entry in a terminal. My idea was to split the frame into 2 columns, with the left column showing commands to be entered and the right column having the explanation.

I haven't got past the first part of a scrolling text (vertical marquee text). I've tried the animate package and it only overlays the earlier text. This is what I tried

\documentclass{beamer}
\usepackage{tikz}
\usepackage{animate}
\begin{document}
  \begin{frame}
    \frametitle{MD5SUM}
     \begin{animateinline}[autoplay,loop]{1}
      cat a
      \newframe
      cat b
      \newframe
      md5sum a
     \end{animateinline}
   \end{frame}
 \end{document}

Now beamer has those overlay specifications but I don't think any one of them is suitable for scrolling text. I don't want to hit enter everytime to uncover the next command, just play the command sequences at a specified rate.

Alternately, is there a better way to achieve this? All the animations are going to be text with probably no images.

UPDATE: With Alexander's answer, I tried to use it with Beamer with mixed results.

I've prepared a minimal example where the previous animateinline text is coming through to the next slide. Can anyone tell me, what exactly is going on and where I'm incorrect?

 \documentclass{beamer}
  \usepackage{beamerthemeWarsaw}
  \usepackage{animate}
  \usepackage{expl3}
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %command for simulating terminal in/output
  %\scroll{<width as TeX dim>}{<number of lines>}{terminal text line}
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  \ExplSyntaxOn
  \seq_new:N\g_linebuffer_seq
  \newcommand\scroll[3]{
    \ttfamily
    \seq_gput_right:Nx\g_linebuffer_seq{#3}
    \int_compare:nT{\seq_length:N\g_linebuffer_seq>#2}{
    \seq_gpop_left:NN\g_linebuffer_seq\dummy
    }
    \fbox{\begin{minipage}[t][#2\baselineskip]{#1}
    \seq_map_inline:Nn\g_linebuffer_seq{\mbox{##1}\\}
    \end{minipage}}
    }
  \ExplSyntaxOff

\begin{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{frame}[fragile]
  \frametitle{Random records}
  How do you randomly select, say 1000 records from a large file?  Use
  the \verb|shuf| command
  \begin{animateinline}[autoplay,loop]{1}
    \scroll{0.8\linewidth}{10}{ cat a}%
    \newframe
     \scroll{0.8\linewidth}{10}{clear}
    \end{animateinline}
%
\end{frame}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{frame}[fragile]
  \frametitle{Checking for Duplicate Files}
  How do you quickly check whether 2 files are the same? Use the
  \verb|md5sum| command.
  \begin{animateinline}[autoplay,loop]{1}
    \scroll{0.8\linewidth}{8}{\# echo 1 2 3 > a}
    \newframe
    \scroll{0.8\linewidth}{8}{clear}
  \end{animateinline}
\end{frame}
\end{document}

Best Answer

For simulating terminal output you will have to maintain a line buffer. In the example below I am using the sequence data type of LaTeX3 for this purpose. A minipage of given width and number of lines height is used to mimick the terminal window. If the number of lines in the buffer goes past the height of the minipage, the top line in the buffer is removed.

EDIT: New command \clearbuf clears the line buffer. Recommended use before first \scroll in an animateinline environment and to simulate clear (screen) shell command.

EDIT 2: This new version allows for ''multiline'' text as argument to \scroll. However, line breaks must be explicitly denoted in the argument using a marker string. The default is §§ which is rarely seen on terminals. But any user defined sequence of letters is possible. User defined marker strings must be passed as optional first argument, enclosed between [ and ] to \scroll.

EDIT 3: Note that macros within the text argument of \scroll are expanded before being added to the line buffer. The reason is to make macros which are called more than once but change their content between calls to \scroll, such as the line numbers \i in the example, work correctly.

Use \noexpand if you want to defer expansion of macros to the time when the whole line buffer is typeset. This is useful for escaping special characters or for text formatting macros (colour, typeface). See the updated example.

\documentclass[dvisvgm]{article}
\pagestyle{empty}

\usepackage{courier}
\usepackage{animate}  
\usepackage{expl3}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
%commands for simulating terminal in/output  
%\scroll[<line separator string>]{<width as TeX dim>} 
%                             {<number of lines>}{terminal text line}  
%\clearbuf  %clears line buffer  
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
\ExplSyntaxOn
\seq_new:N\g_linebuffer_seq
\seq_new:N\g_inputline_seq
\newcommand\scroll[4][§§]{
  \seq_set_split:Nnn\g_inputline_seq{#1}{#4}
  \seq_map_inline:Nn\g_inputline_seq{
    \seq_gput_right:Nx\g_linebuffer_seq{##1}
    \int_compare:nT{\seq_count:N\g_linebuffer_seq>#3}{
      \seq_gpop_left:NN\g_linebuffer_seq\dummy
    }
  }
  \fbox{\begin{minipage}[t][#3\baselineskip]{#2}
    \ttfamily
    \seq_map_inline:Nn\g_linebuffer_seq{\mbox{##1}\\}
  \end{minipage}}
}
\newcommand\clearbuf{\seq_gclear:N\g_linebuffer_seq}
\ExplSyntaxOff

\begin{document}  

  \begin{animateinline}[controls,loop]{1}  
      \scroll{0.9\linewidth}{8}{cat a}  
    \newframe 
      \scroll{0.9\linewidth}{8}{cat b} 
    \newframe 
      \scroll{0.9\linewidth}{8}{John§§Linda§§Albert§§Francis}  
    \newframe 
      \scroll{0.9\linewidth}{8}{ln -s a empty.txt}
    \newframe 
      \scroll{0.9\linewidth}{8}{md5sum empty.txt}
    \newframe
      \scroll{0.9\linewidth}{8}{d41d8cd98f00b204e9800998ecf8427e  empty.txt}
    \newframe 
      \scroll{0.9\linewidth}{8}{md5sum b}
    \newframe
      \scroll{0.9\linewidth}{8}{88a1d2cf7920275378bebdf438bae941  b}
    \newframe 
      \scroll{0.9\linewidth}{8}{clear}\clearbuf  
    \newframe 
      \scroll{0.9\linewidth}{8}{}\clearbuf
    \newframe  
    \multiframe{10}{i=1+1}{
       \scroll{0.9\linewidth}{8}{\noexpand\# Line \i}
    }  
    \newframe 
      \scroll{0.9\linewidth}{8}{clear}\clearbuf  
    \newframe 
      \scroll{0.9\linewidth}{8}{}\clearbuf
  \end{animateinline}  

  \begin{animateinline}[controls,loop]{1}  
      \clearbuf\scroll{0.9\linewidth}{8}{cd /usr/bin}
    \newframe  
      \scroll{0.9\linewidth}{8}{echo \noexpand\$PWD} 
    \newframe 
      \scroll{0.9\linewidth}{8}{/usr/bin}
    \newframe
      \scroll{0.9\linewidth}{8}{ls -l md5sum} 
    \newframe 
      \scroll{0.9\linewidth}{8}{% 
        -rwxr-xr-x 1 root root 30172 Dec 14 2010
        \noexpand\bfseries\noexpand\color{red}md5sum
      } 
    \newframe
      \scroll{0.9\linewidth}{8}{clear}\clearbuf
    \newframe 
      \scroll{0.9\linewidth}{8}{}\clearbuf
  \end{animateinline}  

\end{document}