2

I'm trying to remove all id=" "s from an .html file but I'm not sure where I am going wrong. I tried using regular expressions but all I am getting is the .html file rendering in my Ubuntu terminal.

Code:

grep -Ev '^$id\="[a-zA-Z][0-9]"' *.html

I am executing it with bash ex.sh.

9

Though this goes against my better judgment I'll post it (sed part).

That is: if it is for a quick and dirty fix go ahead. If it is a bit more serious or something you are going to do frequently etc. Use something else like python, perl etc. where you do not rely on regular expressions, but rather modules to handle HTML documents.

One of the simpler ways would be to use e.g. sed.

sed 's/\(<[^>]*\) \+id="[^"]*"\([^>]*>\)/\1\2/' sample.html > noid.html

Explained:

            +--------------------------------- Match group 1
            |                      +---------- Match group 2
         ___|___                ___|___
        |       |              |       |  
sed 's/\(<[^>]*\) \+id="[^"]*"\([^>]*>\)/\1\2/' sample.html > noid.html
     |   |  | |   |  |    | ||    |  |      |
     |   |  | |   |  |    | ||    |  |      +- \1\2  Subst. with group 1 and 2
     |   |  | |   |  |    | ||    |  +-------- >     Closing bracket
     |   |  | |   |  |    | ||    +----------- [^>]* Same as below
     |   |  | |   |  |    | |+---------------- "     Followed by "
     |   |  | |   |  |    | +----------------- *     Zero or more times
     |   |  | |   |  |    +------------------- [^"]  Not double-quote
     |   |  | |   |  +------------------------ id="  Literal string
     |   |  | |   +---------------------------  \+   Space 1 or more times
     |   |  | +------------------------------- *     Zero or more times 
     |   |  +--------------------------------- [^>]  Not closing bracket
     |   +------------------------------------ <     Opening bracket
     +---------------------------------------- s     Substitute

Use sed -i to edit file in place. (Regrets possible but no undo.)


Better; example using perl:

#!/usr/bin/perl

use strict;
use warnings;

use HTML::TokeParser::Simple;
use HTML::Entities;
use utf8;

die "$0 [file]\n" unless defined $ARGV[0];

my $parser = HTML::TokeParser::Simple->new(file => $ARGV[0]);

if (!$parser) {
    die "No HTML file found.\n";
}

while (my $token = $parser->get_token) {
    $token->delete_attr('id');
    print $token->as_is;
}

Your grep command would match nothing. But as you use the invert option -v it prints everything not matching – thus the entire file.

grep is not a in place file modifier but normally a tool for finding stuff in file(s). Try e.g.:

grep -o '\(<[^>]*\)id="[^"]*"[^>]*>' sample.html

-o means print only matching pattern. (Not whole line)

sed, awk etc. are often used to edit streams or files. E.g. as by example above.


From your grep there is a few miss-conceptions:

 id\="[a-zA-Z][0-9]"

Would match exactly:

  1. id=
  2. One character in the range a-z or A-Z
  3. Followed by one single digit

In other words it would match:

id="a0"
id="a1"
id="a2"
...
id="Z9"

Nothing like: id="foo99" or id="blah-gah".


Further it would match:

 ^ <-- start of line (As it is first in pattern or group)
 $ <-- end of line   (As you use the `-E` option)
 # Else it would be:
 ^ <-- start of line (As it is first in pattern or group)
 $ <-- dollar sign   (Does not mean end of line unless it is at end of
                      pattern or group)

Thus nothing.

  • 1
    man that's awesome! – DᴀʀᴛʜVᴀᴅᴇʀ Apr 18 '13 at 19:29
  • minor note: the sed example will only transform the first match on a line, add a /g on the end to transform all tags found in a single input line. If you have mixed case tags, add an i in there as well to be case-insensitive. (s/.../.../ig) – troyengel Oct 20 '18 at 18:01
1

I'm not seriously suggesting this, but I worked out how to do it with an XSLT processor that accepts html. Run with xsltproc --html strip-html-id.xslt input.html

<!-- strip-html-id.xslt -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="html" doctype-system="about:legacy-compat" />

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@id" />

</xsl:stylesheet>
0

As mentioned in another answer you can use a Ruby one-liner to parse HTML. For example, you can use the following:

ruby -rnokogiri -e 'doc = Nokogiri::HTML(readlines.join); doc.xpath("//@id").remove; puts doc' sample.html

This line parses the file given as argument, sample.html, strips it of all id attributes and prints the output. If sample.html is

<!DOCTYPE html>
<html>
  <body>
    <h2 id="section1">Section 1</h2>
    <h2 id="section2">Section 3</h2>
    <h2>Section 4</h2>
    <h2 id="section5">Section 5</h2>
  </body>
</html>

it outputs

<!DOCTYPE html>
<html><body>
    <h2>Section 1</h2>
    <h2>Section 3</h2>
    <h2>Section 4</h2>
    <h2>Section 5</h2>
  </body></html>

Note that by using Nokogiri::HTML() will put the content inside html and body if it is not already in such a structure and it will also add a DOCTYPE. If you want to strip html, body and DOCTYPE or do not want them added you can use

ruby -rnokogiri -e 'doc = Nokogiri::HTML.fragment(readlines.join); doc.search("@id").remove; puts doc' sample.html

which for the same input file will output

    <h2>Section 1</h2>
    <h2>Section 3</h2>
    <h2>Section 4</h2>
    <h2>Section 5</h2>
  • Is there a bug in that? It strips of DOCTYPE, head etc. and put it all in body tag. It also reformats document. – Runium Apr 18 '13 at 20:28
  • @Sukminder I made an edit shortly after I posted. Now it does not wrap it in anything. – N.N. Apr 19 '13 at 5:02
  • Thanks for feedback, but it is with your current line. Spec: ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux], Nokogiri (1.5.5). Ran it on the HTML for this site. (As in this: unix.stackexchange.com/questions/72917/…) – Runium Apr 19 '13 at 14:22
  • @Sukminder I have edited to show the difference of parse and fragment. – N.N. Apr 19 '13 at 14:42
  • No. If the <body> tags are in column 1 (no space before it on the line) – it is not stripped using fragment. If there is a space or a tab etc. in front of it then it is. It also moves such things as meta, title, link to css and javascript down to body when not stripped. Here is a sample. – Runium Apr 19 '13 at 15:24

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.