1

I'd like to build a bash (or something else) script that when run will search it's own directory (recursively) for files with the html extension, and change in them all the code between specific tags with the content from another (specific) file.

I'm doing this because I need to have a navigation across all pages but it has small changes in each one that will be taken care of later, so this script would help me to just get the base first, so I can then alter the rest.

I'm specifically looking to do this with a script, locally, not to replace it with javascript or php code in the html.

Brad
  • 159,648
  • 54
  • 349
  • 530
user137369
  • 5,219
  • 5
  • 31
  • 54
  • Use some sort of server-side includes. Much easier and more predictable than what you are trying to do. – Brad Sep 15 '12 at 17:54
  • The navigation **will** have small changes in each page, and the files will be viewed locally. Any kind of workaround that just includes the file in there is a no go. – user137369 Sep 15 '12 at 17:56
  • Small changes are no problem on a server, but yes, it makes perfect sense that if these are to be viewed locally, they must be good to go. Might I suggest writing a script to assemble these files when ready? HTML can't really reliably handled by regex (although it might work for a specific case for you), making this a difficult problem. Can you at least use custom tags identifying the insertion points? Maybe HTML comments ` – Brad Sep 15 '12 at 18:00
  • Yes, I was thinking of doing something like ``, or between two specific comments, to be as unambiguous as possible. I seem to recall a few years back seeing something with `cat` where it could just take the contents of a full external file without having to worry about stuff like escaping characters. I was thinking among those lines. – user137369 Sep 15 '12 at 18:06

2 Answers2

3

This should get you started. It will directly edit the files, so make a backup or remove the --in-place switch temporarily. Create the following four files:

  1. replace.sh:

    #!/bin/bash
    find -name '*.html' | while read FILE; do
            sed --in-place -f replace.sed "$FILE"
    done
    
  2. replace.sed:

    /{{NAV}}/{
        r replacement
        d
    }
    
  3. replacement:

    <ul>
        <li>Here</li>
        <li>There</li>
    </ul>
    
  4. file.html

    <html>
    <head></head>
    <body>
        <nav>
                {{NAV}}
        </nav>
        <h1>Welcome to Whatever!</h1>
    </body>
    

Make replace.sh executable (chmod a+x replace.sh). Test it by running replace.sh and checking the contents of file.html

$ ./replace.sh && cat file.html
<html>
<head></head>
<body>
<nav>
<ul>
<li>Here</li>
<li>There</li>
</ul>
</nav>
<h1>Welcome to Whatever!</h1>
</body>

It's important that the placeholder {{NAV}} is on its own line, because the sed script will obliterate the contents of that line. The placeholder should be changed if "{{NAV}}" is, for whatever reason, likely to appear in your files.

Ben Graham
  • 2,089
  • 17
  • 21
  • Tried to edit your *find* command to include the directory to search for as “.” (OSX gives me an error, it it's not present), but “edits need to be at least six characters”. – user137369 Sep 21 '12 at 15:57
  • It seems to work great, it's almost what I want, just one detail missing. How can I edit *replace.sed* so it replaces text between two strings? Say, for example, between {{startNAV}} and {{endNAV}}. – user137369 Sep 21 '12 at 15:59
  • You can replace the regex part of replace.sed with `/.*{{startNAV}}.*{{endNAV}}.*/`. however this will still delete the whole line. I'm assuming if you're inserting placeholders into your files then it's no problem to get them on their own line. If you're not using placeholders and are isntead going to swap `{{startNAV}}` in the regex with a HTML tag with already exists in your files, this is not a great idea. See http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 – Ben Graham Sep 23 '12 at 01:29
  • Since I've asked the original question, I've started using HAML, so maybe regex will work better with that than with HTML, but still, there was some miscommunication. I want it to replace text between two strings, yes, but they're multi-line (your answer only works single line, as it is). The ideia is I'll have `{{startNAV}} (a bunch of lines go here) {{endNAV}}`. That way, it I include {{startNAV}} and {{endNAV}} in the file that has the replacement, I can always reuse the script on the same files. I'll replace actual content, {{startNAV}} and {{endNAV}} are the (block of) lines delimiters. – user137369 Sep 23 '12 at 16:21
  • If you're open to switching to something like HAML, I really recommend trying any template language with the idea of includes or inheritance such as Jinja2. It solves exactly the problem that you've got and will be a far more robust than regex, not to mention more understandable than esoteric sed commands should somebody else need to look at the work :) Unfortunately my off-the-cuff sed knowledge won't stretch that regex over several lines so unfortunately I'll just have to leave you with that recommendation, and this example of how it might look in jinja2: http://pygments.org/demo/55345/ – Ben Graham Sep 23 '12 at 22:25
  • I was thinking about trying MiddleMan (more inclined towards ruby, since HAML and SASS use it), do you know how it compares? Anyway, you were a great help, and I'll try to find how to do it multi-line. Thank you for the effort put into this, it may be useful for someone still, so I'll mark it as an accepted answer and vote up. – user137369 Sep 24 '12 at 10:09
2

Based on the great help given by Ben Graham, and a bit more of research, here is the answer to what I was looking for.

You'll need one file to serve as the script (replace.sh), another to serve as the replacement (replacementFile), and any other number of files that will have the replacement applied to them.

The script. replace.sh

#!/bin/bash
find . -name "*.html" | while read FILE; do
    sed -i '' '/{{endSTRING}}/r replacementFile
    /{{startSTRING}}/,/{{endSTRING}}/d' ${FILE}
done

And that's it. Some extra points, though. replacementFile should have an empty line at the end, or it'll connect with the next line on the altered file. -i '' is there to provide no backups; if you want them, just place something between the quotes, and it'll copy the original files with whatever you chose appended.

user137369
  • 5,219
  • 5
  • 31
  • 54