I have a large directory with multiple levels and tons of files. A majority (but not all) of these files are HTML files, but do not have the .html extension (a problem when copying over to Windows).

All of the HTML files have this as a first line:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

How can I, by just using a command, add the .html extension on to all files containing this string?

I have seen this SO question, but it does not rename based on whether the containings a string.

  • You could easily write a perl or shell script to do this. What shell are you using?
    – Peschke
    Mar 19, 2017 at 2:03
  • What is the expected format of the filenames without the .html extension. Is it possible they may have a dot . elsewhere in their name?
    – MikeD
    Mar 19, 2017 at 2:13
  • @Peschke I am using the bash shell on Ubuntu.
    – esote
    Mar 19, 2017 at 2:37
  • @MikeD I cannot be sure of the extensions or names of other files. Some files will have dots in their names (some end with .jpg, for example), while others will be extensionless (on purpose, and are not HTML files). The only thing I can use to rename files is the fact that they all have the HTML header in their first line.
    – esote
    Mar 19, 2017 at 2:40

2 Answers 2


Assuming all of the non-extension .html files have no extension at all, here is a simple bash script to accomplish what you need.

find . -type f ! -iname "*.html" -a ! -iname "*.htm" | while IFS= read -r file; do
    head -1 "${file}" | if grep -q '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' ; then 
        mv "${file}" "${file}.html" 

This script does the following, in order:

  1. Recursively finds all files in your current directory and child directories that do not have the .html extension.
  2. Checks to see if the found files first line is the string you provided.
  3. If the first line matches, the file is renamed to include the .html extension.
  • "Assuming all of the non-extension .html files have no extension at all": while all HTML files do not have extensions, some files (such as images or CSS files) do have extensions. Will this cause a problem when using your script?
    – esote
    Mar 19, 2017 at 2:42
  • 1
    @idempotence: Only files without an extension and start with the <!DOCTYPE html ... line you provided will be affected. As long as the other files do not start with that line, they will not be renamed. I recommend running this script on a subset of files first as a test.
    – Peschke
    Mar 19, 2017 at 2:45
  • Amazing! This script worked in exactly the manner I had wished. Thanks so much!
    – esote
    Mar 19, 2017 at 2:56
  • Perfect! You are very welcome. Glad to help. :)
    – Peschke
    Mar 19, 2017 at 2:58
  • I came up with something similar. Maybe use -iname in the find? Also, if it's possible to see .htm extensions, as well, you may want to update the find as find . -type f ! -iname "*.html" -a ! -iname "*.htm"
    – MikeD
    Mar 19, 2017 at 2:58
goldn='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' \
find . -type f \
   ! -name  '*.[hH][tT][mM]'     \
   ! -name '.*.[hH][tT][mM]'     \
   ! -name  '*.[hH][tT][mM][lL]' \
   ! -name '.*.[hH][tT][mM][lL]' \
-exec sh -c '
   shift "$1"
   while case $# in 0 ) break;; esac
      read l1 < $1
      case $l1 in "$goldn" ) mv "$1" "$1.html";; esac
' 2 1 {} +

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .