[Tex/LaTex] Bash lstlistings treats “$#” as a comment

listingsparsing

Background: In Bash, $# represents the number of arguments, and # starts a comment.

Consider the following Bash script:

# This is a comment
while [[ $# -gt 0 ]]; do
    echo $1
    shift
done

Note that the syntax coloring by StackExchange here is incorrect! The # in $# is being treated as a comment marker.

Unfortunately, the listings package gives me the same result:

Example of incorrect syntax parsing
     
The same example with a different style

\documentclass{article}

\usepackage{listings}
\lstset{language=Bash}

\pagenumbering{gobble}

\begin{document}

\begin{lstlisting}
# This is a comment
while [[ $# -gt 0 ]]; do
    echo $1
    shift
done
\end{lstlisting}

\end{document}

How can I fix this and get listings to display the right output?

Best Answer

Whoever wrote the listings language for bash probably didn't anticipate this case. Actually, the listings package is a far cry from a proper lexical analyser and doesn't offer a clean way of doing this kind of syntax highlighting. As far as listings' Bash language is concerned, a # character encountered in normal "processing" mode starts a comment, and that is it.

An easy, if ugly, fix is to use the literate key to replace all instances of the $# pattern by... well... itself... in order to prevent the # character from starting a comment if preceded by $:

enter image description here

Unfortunately, this trick has side effects: if columns=fullflexible is used, space characters following $# (if any) get gobbled, which is undesirable. One way to fix this is to use the keepspaces option, also.

Update: Actually, the workaround for preventing spaces from getting gobbled that Manuel mentions in his answer is preferable to setting keepspaces; have a look.

\documentclass{article}

\usepackage{listings}

\lstset{
  language = Bash,
  literate = {\$\#}{{{\$\#}}}2,
  columns  = fullflexible,
  keepspaces,
}

\pagenumbering{gobble}

\begin{document}

\begin{lstlisting}
# This is a comment
while [[ $# -gt 0 ]]; do
    echo $1
    shift
done
\end{lstlisting}

\end{document}
Related Question