Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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.

share|improve this question
2  
Parsing/modifying HTML is not to be done with regular expressions. –  BatchyX Apr 18 '13 at 16:31
    
better alternative? awk or sed? –  Gramps Apr 18 '13 at 16:32
    
Is this HTML(i.e. SGML) or XHTML ? –  BatchyX Apr 18 '13 at 16:34
    
just simple .html. Its been awhile but something like awk '// || // *.html > *.html comes to mind. –  Gramps Apr 18 '13 at 16:39
2  
You can't parse HTML with regex. –  mrb Apr 18 '13 at 19:25
show 1 more comment

3 Answers

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.

share|improve this answer
1  
man that's awesome! –  Gramps Apr 18 '13 at 19:29
add comment

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>
share|improve this answer
add comment

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>
share|improve this answer
    
Is there a bug in that? It strips of DOCTYPE, head etc. and put it all in body tag. It also reformats document. –  Sukminder 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/…) –  Sukminder 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. –  Sukminder Apr 19 '13 at 15:24
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.