18

I have a file with comments:

foo
bar
stuff
#Do not show this...
morestuff
evenmorestuff#Or this

I just want to print all the uncommented code:

foo
bar
stuff
morestuff
evenmorestuff

Being able to strip comments out of a file is so important... What is a good way to do it?

  • 1
    you cannot remove parts of a line with grep. you can use sed for this – miracle173 Sep 24 '14 at 20:12
  • 2
    Your text and your example contradict. You write about lines being commented out, but clearly from the last line you mean line parts. And then the first line with a comment is deleted including EOL, and second second might be, but it is not clear as that is the last line. Please rephrase 'lines commented out' to be exact and disambiguate your examples. – Anthon Sep 25 '14 at 3:12
  • 4
    try using awk -F\# '$1!="" { print $1 ;} ' . – Archemar Sep 25 '14 at 7:02
  • 2
    How would a line like echo '#' # output a # be handled? – Kusalananda Jun 29 '17 at 12:38
  • 2
    @Questionmark I might be clever, but I'm not writing-a-shell-grammar-parser clever. – Kusalananda Jun 29 '17 at 15:29

12 Answers 12

38

One way to remove all comments is to use grep with -o option:

grep -o '^[^#]*' file

where

  • -o: prints only matched part of the line
  • first ^: beginning of the line
  • [^#]*: any character except # repeated zero or more times

Note that empty lines will be removed too, but lines with only spaces will stay.

  • 2
    I would use grep -v '^#' file > newfilewithoutcomments – Basile Starynkevitch Jun 29 '17 at 12:30
  • 1
    It should be noted this is NOT a general method for shell scripts, as for example the line somvar='I am a long complicated string ## with special characters' # and I am a comment will not be handled correctly. – Wildcard Sep 6 '17 at 21:45
  • This variant works better for me (on a Mac): grep -o '^[^#].*' file – Pierz May 8 '18 at 10:12
  • The comments are gone but I'm seeing a bunch of white space in their place in the output? sed solution only has one blank line, seems like a solid argument to use other answer, unless I'm missing something? – JBallin Jul 18 '18 at 22:55
  • @JBallin Did you define some alias for grep maybe? Try changing grep to command grep, if you still see spaces post the sample input. – jimmij Jul 19 '18 at 0:33
31

I believe sed can do a much better job of this than grep. Something like this:

sed '/^[[:blank:]]*#/d;s/#.*//' your_file

Explanation

  • sed will by default look at your file line by line and print each line after possibly applying the transformations in the quotes. (sed '' your_file will just print all the lines unchanged).
  • Here we're giving sed two commands to perform on each line (they're separated by a semicolon).
  • The first command says: /^[[:blank:]]*#/d. In English, that means if the line matches a hash at its beginning (preceded by any number of leading blanks), delete that line (it will not be printed).
  • The second command is: s/#.*//. In English that is, substitute a hash mark followed by as many things as you can find (till the end of the line, that is) with nothing (nothing is the empty space between the final two //).
  • In summary, this will run through your file deleting lines that consist entirely of comments and any lines left after that will have the comments stricken out of them.
  • It will also delete anything found after a hash inside a string, no ? E.g. mystring="Hello I am a #hash" will become mystring="Hello I am a" – javadba Mar 12 '17 at 16:11
  • @javadba, yes, but at that point you might as well use a full parser. What's going to be using this data that can understand quotes and variable assignments but can't handle comments? (This is why many config files such as crontab only allow full-line comments, with or without leading whitespace, but do not allow trailing comments on a line. The logic is MUCH simpler. Use only the first of the two Sed instructions in this answer for a crontab comment stripper.) – Wildcard Sep 6 '17 at 22:01
  • great answer, this looks like a great balance of utility vs. complexity for a wide array of general use-cases, but in the case that you know ahead of time that you only need to delete lines starting directly with # (in column 1), is there any benefit to sed over grep -v "^#" ? – RBF06 Jan 15 at 16:36
4

You can achieve the required output using sed command. The below command had done the trick for me.

sed 's/#.*$//g' FileName

Where

  • #.*$ - Regexp will filter all the string that starts with # up to the end of the line

Here we need to remove those lines so we replaced with empty so skipping 'replacement' part.

  • g - mentioning repeated search of the pattern until end of file is reached.

General syntax of sed: s/regexp/replacement/flags FileName

  • 2
    note: 4th line replaced with new line in this case. – αғsнιη Sep 26 '14 at 7:23
  • 1
    Try that with a script containing that sed command... – Kusalananda Jun 29 '17 at 12:40
3

You can use invert match like this:

    #grep -v "#" filename

-v, --invert-match Invert the sense of matching, to select non-matching lines. (-v is specified by POSIX.)

  • 2
    @alinh Thanks for reviewing the answer. Please note that the question required not only the beginning of the line but anywhere in the file. This is also showing in his/her expected result in the question above. My answer would be incorrect if I only look for beginning of the line. – Raza Sep 25 '14 at 14:19
  • zzz. my bad, didn't see the last line :( – alinh Sep 25 '14 at 14:33
  • 1
    This will completely remove the line starting with evenmorestuff in the OP's example. – Joseph R. Sep 26 '14 at 3:22
  • @JosephR. good catch. I missed that earlier. In this case grep -o '^[^#]*' file would be the best solution. this is already explained by jimmij. thanks for your review – Raza Sep 26 '14 at 17:52
3

I like joseph's answer but needed it to strip // comments also so I modified it slightly & tested on redhat

# no comments alias
alias nocom="sed -E '/^[[:blank:]]*(\/\/|#)/d;s/#.*//' | strings"

# example
cat SomeFile | nocom | less

I bet there's a better way to remove blank lines than using strings but it was the quick & dirty solution I used.

-cheers

3

This worked for me

sed -i.old -E  "/^(#.*)$/d" file 
2
cat YOUR_FILE | cut -d'#' -f1

It uses # as column separator and keeps just the first column (that is everything before #).

  • If YOUR_FILE is a script containing those commands, the script would leave cat YOUR_FILE | cut -' in the file on that line. – Kusalananda Jun 29 '17 at 12:41
2

Use expression like

egrep -v "#|$^" <file-name> 

:-v : will do invert match

:# : will match all lines starting with #

:$^ : will match all the blank lines

  • No, the # will match anywhere on the line, and remove the whole line. – ilkkachu Oct 9 '16 at 19:12
2

The best solution would be to use the command:

sed -i.$(date +%F) '/^#/d;/^$/d' ntp.conf

The -i is the in-place edit but the prefix directly following tells sed to create a backup. In this case with a date extension (ntp.conf.date) We run two commands each with an address space, the first deletes the commented lines and the second, separated from the first by a semi-colon , deletes the blank lines.

I found this solution on : theurbanpenguin.com

2

As others have pointed out, sed and other text-based tools won't work well if any parts of a script look like comments but actually aren't. For example, you could find a # inside a string, or the rather common $# and ${#param}.

I wrote a shell formatter called shfmt, which has a feature to minify code. That includes removing comments, among other things:

$ cat foo.sh
echo $# # inline comment
# lone comment
echo '# this is not a comment'
[mvdan@carbon:12] [0] [/home/mvdan]
$ shfmt -mn foo.sh
echo $#
echo '# this is not a comment'

The parser and printer are Go packages, so if you'd like a custom solution, it should be fairly easy to write a 20-line Go program to remove comments in the exact way that you want.

1

None of the other answers seem to do this justice, they either leave in empty lines, or leave in lines where the comment isn't at the first character. I ended up using this:

cat << EOF >> ~/.bashrc
alias nocom='sed -e "/^\s*#/d" -e "/^\s*$/d"'
EOF

This sets up an alias, so that you don't have to memorize it (which is impossible to begin with). Open a new session, and you'll have the new nocom command. Then you can just

nocom /etc/foobar.conf

Cheers.

  • 1
    there's not much point in matching .*$ in the first regex -- the anchor isn't useful and you're not capturing the matched text to use in a replacement. use just ^\s* – Jeff Schaller Jul 26 '18 at 16:40
1

Following the 2nd answer of Joseph R., I add /^$/d to remove blank line.

sed '/^[[:blank:]]*#/d;s/#.*//;/^$/d'

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.