18

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?

  • 3
    Not sure if it's quite what you want, but \lstset{language = bash, otherkeywords = $\#} will make $# a keyword and so not start a comment. – Joseph Wright Oct 29 '14 at 16:37
  • @JosephWright Ooh, that's very close. It does treat it as a keyword, not a variable (of course), but I'd be willing to make that compromise. – wchargin Oct 29 '14 at 16:39
  • @JosephWright arg I tried that with morekeywords and it didn't work:-) if otherkeywords works I should delete my answer:-) – David Carlisle Oct 29 '14 at 16:39
  • @DavidCarlisle I don't think it's perfect (as noted, it ends up as a keyword not a variable, which is why it's a comment not an answer). – Joseph Wright Oct 29 '14 at 16:42
  • $# should not be a special case. The # character does not introduce a comment if it is immediately preceded by any non-whitespace character or is enclosed in single or double quotes. #this is a comment echo this# is not a comment echo neither is#this for that matter. echo not a comment #but this is X=this is not "a # comment either" – user65285 Oct 29 '14 at 18:04
10

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}
  • This is nice, but it loses the space after $# with columns=fullflexible. – wchargin Oct 29 '14 at 16:50
  • @WChargin Not if you also use the keepspaces option. – jub0bs Oct 29 '14 at 16:50
  • What about literate = {\$\#}{{{\$\#}}}2 {\$\# }{{{\$\# }}}2? – Manuel Oct 29 '14 at 17:09
  • @Manuel Your approach works fine if only one space character follows, but if you have more after that, those will get gobbled. – jub0bs Oct 29 '14 at 17:19
  • @Jubobs But that happens if you don't use keepspaces. By the way, the problem is that, even in the default columns it seems that there's a space, but there isn't, it's like a “part of the glyph”. – Manuel Oct 29 '14 at 17:22
5

If you don't mind adding markup to the listing you can define an escapechar and hide the #

enter image description here

\documentclass{article}

\usepackage{listings}
\lstset{language=Bash,escapechar=^}

\pagenumbering{gobble}

\begin{document}

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

\end{document}
4
\documentclass{article}

\usepackage{listings}

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

\pagenumbering{gobble}

\begin{document}

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

\end{document}
  • I think I see what you meant earlier... – jub0bs Oct 29 '14 at 18:38
  • 1
    :) I tend not to explain myself quite good (I'm not that good at English). The thing is that, unles keepspaces is triggered, many spaces are converted to just one (at least in this example), so they are not “gobbled”. I meant that this (half-)solution is independent of keepspaces. – Manuel Oct 29 '14 at 20:07
2

I can suggest using pythontex. A run of the program pythontex is needed when the listings change. The example uses beramono so boldface is distinguishable in the monospaced font.

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{beramono}

\usepackage[pygopt={style=bw}]{pythontex}

\begin{document}

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

\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.