The easiest way to remove comments from a shell script is to use sed (stream editor for filtering and transforming text) command.

An updated source code is available at GitHub.

Let’s prepare a sample shell script for testing purposes:

$ cat example.sh
#!/bin/bash
# example script

echo "# test";# echo "# test"

# check the first parameter
if [ "$1" = "#" ]; then 
  # test couple of different cases
  echo "#"; # output # character 
  echo '\#'; # output # character '#' for test purpose
  echo \#\#\#; # comment # comment # comment '# comment'
  echo \#
  echo \#;
  echo \#; # comment
fi
# end of the script

To remove comments, execute the following command.

$ sed -e '1{/^#!/ {p}}; /^[\t\ ]*#/d;/\.*#.*/ {/[\x22\x27].*#.*[\x22\x27]/ !{:regular_loop s/\(.*\)*[^\]#.*/\1/;t regular_loop}; /[\x22\x27].*#.*[\x22\x27]/ {:special_loop s/\([\x22\x27].*#.*[^\x22\x27]\)#.*/\1/;t special_loop}; /\\#/ {:second_special_loop s/\(.*\\#.*[^\]\)#.*/\1/;t second_special_loop}}' example.sh
#!/bin/bash

echo "# test";

if [ "$1" = "#" ]; then 
  echo "#"; 
  echo '\#'; 
  echo \#\#\#; 
  echo \#
  echo \#;
  echo \#;
fi

It looks complicated so let’s write sed commands in a more readable way.

$ cat remove_comments.sed
1 {
  /^#!/ {
    p
  }
}

/^[\t\ ]*#/d

/\.*#.*/ {
  /[\x22\x27].*#.*[\x22\x27]/ !{
    :regular_loop
      s/\(.*\)*[^\]#.*/\1/
    t regular_loop
  }
  /[\x22\x27].*#.*[\x22\x27]/ {
    :special_loop
      s/\([\x22\x27].*#.*[^\x22\x27]\)#.*/\1/
    t special_loop
  }
  /\\#/ {
    :second_special_loop
      s/\(.*\\#.*[^\]\)#.*/\1/
    t second_special_loop
  }
}

Very short description:

  1. Always print the first line if it defines an interpreter

  2. Remove “empty” lines with comments

  3. Parse comments

3.1 Remove comments from lines without or characters

3.2 Remove comments from lines with or characters

3.3 Remove comments from lines with an escaped # character

Now you can easily check any applied changes:

$ sed -f remove_comments.sed example.sh
#!/bin/bash

echo "# test";

if [ "$1" = "#" ]; then 
  echo "#"; 
  echo '\#'; 
  echo \#\#\#; 
  echo \#
  echo \#;
  echo \#;
fi

Check out file differences:

$ sed -f remove_comments.sed example.sh | diff -u example.sh -
--- example.sh  2012-11-07 00:42:00.876279927 +0100
+++ -   2012-11-07 00:44:03.340909374 +0100
@@ -1,16 +1,12 @@
 #!/bin/bash
-# example script
 
-echo "# test";# echo "# test"
+echo "# test";
 
-# check the first parameter
 if [ "$1" = "#" ]; then 
-  # test couple of different cases
-  echo "#"; # output # character 
-  echo '\#'; # output # character '#' for test purpose
-  echo \#\#\#; # comment # comment # comment '# comment'
+  echo "#"; 
+  echo '\#'; 
+  echo \#\#\#; 
   echo \#
   echo \#;
-  echo \#; # comment
+  echo \#;
 fi
-# end of the script

Remove comments from a file and store backup with .backup suffix:

$ sed -i.backup -f remove_comments.sed example.sh
ko-fi