Smart way to perform a similar replacement (not simple enough for diff) in multiple files

diff()patchreplacesearchtext processing

I have a bunch of files (45, so too many to edit them manually without going crazy) which all require the same changes (as seen in the three diffs at the end of this question).

Most tools used for search-and-replace on the commandline only support line-by-line replacements so they won't work.

Is there some commandline tool available which can do the job? Basically performing a multi-line regex search&replace with backreferences without shoving the whole regex into a single line (i.e. it should be read from a file or stdin) should be enough, but maybe there's an even better solution to do this…


--- a/editfile.html
+++ b/editfile.html
@@ -60,7 +60,5 @@

 <script type="text/javascript">
-    $(document).ready(function() {
-        editFileInit(opts);
-    });
+    initPage('editFile', opts);
 </script>
 {% endblock %}
diff --git a/gallery.html b/gallery.html
index 4a07f70..81084ad 100644
--- a/exodus/templates/gallery.html
+++ b/exodus/templates/gallery.html
@@ -87,9 +87,7 @@

     <script type="text/javascript">
-        $(document).ready(function() {
-            galleryInit({
-                editTitleUrl: {{ csrf_url_for('gallery_editpictitle')|tojson|safe }},
-                delPicUrl: {{ csrf_url_for('gallery_delpic')|tojson|safe }}
-            });
+        initPage('gallery', {
+            editTitleUrl: {{ csrf_url_for('gallery_editpictitle')|tojson|safe }},
+            delPicUrl: {{ csrf_url_for('gallery_delpic')|tojson|safe }}
         });
     </script>
diff --git a/stream_history.html b/stream_history.html
index 783fea4..ea837fa 100644
--- a/exodus/templates/stream_history.html
+++ b/exodus/templates/stream_history.html
@@ -25,8 +25,6 @@

     <script type="text/javascript">
-        $(document).ready(function() {
-            songHistoryInit({
-                dataURL: {{ url_for('stream_history_json')|tojson|safe }}
-            });
+        initPage('songHistory', {
+            dataURL: {{ url_for('stream_history_json')|tojson|safe }}
         });
     </script>

If the indentation is not preserved it's not a big issue.

Best Answer

Let's keep it simple. The files fit comfortably in memory, so don't bother reading them line by line or anything like that. Perl's -p (iterate over file contents) switch and -0777 “slurp mode” (read a whole file at once, not line by line) are convenient.

To act on multiple files, it's convenient to use -i (edit files in place).

To perform the substitution, use the s substitution operator. See perlre#Modifiers for an explanation of the modifiers msx.

perl -0777 -i.orig -p -e '
    s[
      ^(\s+)                                  # indentation
      \Q$(document).ready(function() {\E\n
      \s*(\w+)Init                            # function name
      (\(((?:(?>[^()]+)|(?3))*)\))            # balanced parentheses
      ;\n\s*\}\);
     ][
       ${1}initPage(\x27$2\x27, $3);
     ]msx;
' **/*.js

**/*.js matches all .js files in the current directory and its subdirectories recursively. This works out of the box in zsh. If you use bash, add shopt -s globstar to your ~/.bashrc.

(Untested, you'll probably have to tweak the regexp a bit.)