-1

I am trying to minify HTML using sed

My problem: I don't want to minify anything inside <pre> tags, but can't make it work..

Here is what I am using:

sed ':a;N;$!ba;s@>\s*<@><@g' $html_file > ${html_file//.html/.minhtml}

This minifies everything, including stuff inside pre tags..

I have looked at using ^[pre] but can't make that work...

I also looked at using sed /skipme/! s/foo/bar/:

sed ':a;N;$!ba; /<pre>\.*<\/pre>/! s@>\s*<@><@g' $html_file > ${html_file//.html/.minhtml}

(...and yes I insist on using sed, not some other tool, thanks.)

  • I also tried this: sed ':a;N;$!ba; /<div class="highlight"><pre>\.*<\/pre><\/div>/! s@>\s*<@><@g' $html_file > ${html_file//.html/.minhtml} – sc0ttj May 19 at 18:26
  • Can you please provide a sample html file to run tests against? – simlev May 20 at 8:04
  • <div class="highlight"> <pre><span></span> <span class="nv">var</span><span class="o">=</span><span class="s2">"foo"</span> <span class="nb">echo</span> <span class="si">${</span><span class="nv">var</span><span class="p">//foo/bar</span><span class="si">}</span> <span class="c1"># outputs 'bar'</span> </pre> </div> – sc0ttj May 20 at 22:30
0

You are aware that regular expressions are the wrong tool for HTML parsing, that it's easy to create edge cases to make a script fail, but you insist in using the wrong tool? Okay, then.

Let's look at cases to be covered: There can be

  • lines without any preformatted text (further called pre),
  • a line of pre,
  • some pre inside a line,
  • more than one pre inside a line,
  • a pre over more than one line and even
  • a pre starting in a line where the previous pre ended.

All those cases are in this example file:

<x>    </x>
<pre>_ _</pre>
_ <pre>_</pre> _<x>    </x>_
_ <pre>_</pre> _<x>    </x> _ <pre>_</pre> _
_ <pre>_<x>    </x>_
_</pre> _
_<x>    </x>_<x>    </x>_
_ <pre>_
_<x>    </x>_<x>    </x>_
_</pre> _ <pre>
_
<x>    </x>_
</pre>

To avoid multiple implementation of the minification part, let's separate pre and non-pre linewise in a first pass:

sed -z -e 's/<pre>/\n&/g;s_</pre>_&\n_g'

(You are obviously using GNU sed, otherwise your :a;N;$!ba; would not work. But for GNU sed, you can drop that code and use option -z instead.) Now this code adds a line break before each <pre> and after each </pre>. Piping that to a second sed gives us much less cases to care for (and line breaks do no harm outside <pre>).

sed -e '\_<pre>.*</pre>_b' -e '/<pre>/,\_</pre>_!s/>\s*</></g'

The first part jumps to the end of the script for lines with single-line pre content; the second part does the minification dummy for the remaining lines, except (!) for multi-line pres.

Together,

sed -z -e 's/<pre>/\n&/g;s_</pre>_&\n_g' file.html | sed -e '\_<pre>.*</pre>_b' -e '/<pre>/,\_</pre>_!s/>\s*</></g'

produces

<x></x>

<pre>_ _</pre>

_ 
<pre>_</pre>
 _<x></x>_
_ 
<pre>_</pre>
 _<x></x> _ 
<pre>_</pre>
 _
_ 
<pre>_<x>    </x>_
_</pre>
 _
_<x></x>_<x></x>_
_ 
<pre>_
_<x>    </x>_<x>    </x>_
_</pre>
 _ 
<pre>
_
<x>    </x>_
</pre>

and – voilá – spaces removed outside pre, but untouched inside.

  • Replacing s/o/O/g with s@>\s*<@><@g makes it stop working - no whitespace removed ... Using s/o/O/g works as expected, but it's useless.. The last part of your sed command doesn't use -z, so will also fail to handle removing \n, as I understand it.. What would go in the last part if something like s@>\n*\s*\n*<@><@g won't work? Did you actually test minifying any HTML using your example, or just changing "o"s to "O"s? – sc0ttj May 20 at 22:28
  • My dictonary doesn't know your definition of "minify", but this code can do anything on lines outside pre. If it's about removal of spaces between tags (looks equally useless to me), my example didn't fail, it just didn't contain anything to be removed (-; I changed the example accordingly. – Philippos May 21 at 7:57
  • My definition of "html minification" is quite standard, I think - removal of leading whitespace, trailing whitespace for each line, then removal of newlines... with the added caveat that I want the contents of <pre> tags ignored, cos that is their purpose .. Sorry if I am not being clear, but it should be easy to see what I mean by looking at the first sed command in my OP - the output does what I need, EXCEPT it does not ignore the contents of <pre> tags.. – sc0ttj May 21 at 14:05

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.