Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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.


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

I am executing it with bash ex.sh.

share|improve this question
Parsing/modifying HTML is not to be done with regular expressions. – BatchyX Apr 18 '13 at 16:31
better alternative? awk or sed? – Darth_Vader 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. – Darth_Vader Apr 18 '13 at 16:39
You can't parse HTML with regex. – mrb Apr 18 '13 at 19:25
up vote 7 down vote accepted

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


            +--------------------------------- 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:


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) {
    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:


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:


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
man that's awesome! – Darth_Vader Apr 18 '13 at 19:29

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:apply-templates select="node()|@*"/>

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

share|improve this answer

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>
    <h2 id="section1">Section 1</h2>
    <h2 id="section2">Section 3</h2>
    <h2>Section 4</h2>
    <h2 id="section5">Section 5</h2>

it outputs

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

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. – 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 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.