Is there a way to show only uncommented lines in a text file/script?

Well, that depends on what you mean by comments. If just lines without a # then a simple:

grep -v '#'

might suffice (but this will call lines like echo '#' a comment). If comment lines are lines starting with #, then you might need:

grep -v '^#'

And if comment lines are lines starting with # after some optional whitespace, then you could use:

grep -v '^ *#'

And if the comment format is something else altogether, this answer will not help you.


Just grepping will never be able to remove all comments (or comments only) because grep does not understand the language that it is going through. To understand what is a comment and what isn't one you need a lexer that understand that particular language.

There are several answers on SO about how to remove all comments from specific programming languages. I'll add two examples here.

For C the answer by Josh Lee argues:

gcc -fpreprocessed -dD -E test.c

Which runs the preprocessor but keeps the macros.

For python the answer by unutbu (with a small adaptation by myself) writes a small lexer using tokenize:

import tokenize
import io
import sys

def nocomment(s):
    result = []
    g = tokenize.generate_tokens(io.BytesIO(s).readline)  
    for toknum, tokval, _, _, _  in g:
        # print(toknum,tokval)
        if toknum != tokenize.COMMENT:
            result.append((toknum, tokval))
    return tokenize.untokenize(result)

print(nocomment(sys.stdin.read()))

You can then write one of these for each programming language and use a case. Assuming that the python lexer is called remove-comments.py

#!/bin/sh
case "$1" in
  *.py)
    remove-comments.py < "$1"
    break
    ;;
  *.c|*.C|*.cc)
    gcc -fpreprocessed -dD -E "$1"
    break
    ;;
  *)
    echo I do not know how to remove comments from $1, sorry
    break
    ;;
esac

Give a name to the script and add the lexers for the languages you need/use. This should be a more-or-less robust design for comment removal from different file types. (Using file instead of a case on filenames would be more robust too).


grep -v "^#" your_file | grep -v "^$" | less

Remove the lines starts with "#" and also remove the empty lines, than send the result to less for a better display.