1

I want to automatically comment out a code block in PHP file, as below:

The original block:

    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);

The new block with comments:

    /* For the production version, the following codelines are commented
       out
    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    */

So I plan to put these lines in two files and use sed to perform the replacement automatically. However, after searching online, I only find Replace string with contents of a file using sed and sed - replace string with file contents, which means either only the source or destination pattern is in one file, and the remaining is in online. But no sample for both in files.

So, how to perform the replacement? Should I use sed or awk?

alancc
  • 213
  • Do you want to only comment out this specific section? With this exact content? – aviro Mar 17 '24 at 09:46
  • @aviro, yes, correct – alancc Mar 17 '24 at 11:14
  • 1
    @EdMorton, Sorry for the mistake. Actually yours and using patch both work. I originally think I can accept multiple answers as solutions, but it is not the case, once I select one, then the other one is unselected. Now I have reselect yours as the solution. – alancc Mar 21 '24 at 15:24
  • Sounds good. Yes, I saw the patch version would work too, it just took me by surprise seeing you accept an answer that didn't work over one of the ones that do. – Ed Morton Mar 21 '24 at 16:08

3 Answers3

3

I suggest you use the patch utility for this purpose.

1. Create the patch file using a diff command

Assuming you have two files, the one with the block you want to replace:

$ cat toreplace.txt 
    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);

And the other one with the block you want to replace:

$ cat replacewith.txt 
    /* For the production version, the following codelines are commented
       out
    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    */

You create a context diff between them and place the content to a patchfile:

$ diff -c toreplace.txt replacewith.txt > patchfile
$ cat patchfile
*** toreplace.txt       2024-03-17 12:12:31.073270945 +0200
--- replacewith.txt     2024-03-17 12:12:45.276887865 +0200
***************
*** 1,4 ****
--- 1,7 ----
+     /* For the production version, the following codelines are commented
+        out
      //  Enable all errors
      ini_set('display_startup_errors', 1);
      ini_set('display_errors', 1);
      error_reporting(E_ALL);
+     */

2. Applying the patch

Now consider this is the original file:

$ cat myfile
line before 1
line before 2
line before 3
    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
line after 1
line after 2 
line after 3

You run the patch command on the file you want to change using the patchfile you've created before.

$ patch -cb myfile patchfile
patching file myfile
Hunk #1 succeeded at 4 (offset 3 lines).
  • the -c flag to "Interpret the patch file as a context difference (the output of the utility diff when the -c or -C options are specified).".
    • It isn't strictly required, because without it "The patch utility shall attempt to determine the type of the diff listing, unless overruled by a -c, -e, or -n option."
  • The -b option to " Save a copy of the original contents of each modified file, before the differences are applied, in a file of the same name with the suffix .orig appended to it."
    • You can remove this flag if you don't want to create a backup.

3. Validation

Now comparing the original file with the patched one:

$ diff -c myfile{.orig,}
*** myfile.orig 2024-03-17 13:00:24.936142831 +0200
--- myfile      2024-03-17 13:13:48.882669202 +0200
***************
*** 1,10 ****
--- 1,13 ----
  line before 1
  line before 2
  line before 3
+     /* For the production version, the following codelines are commented
+        out
      //  Enable all errors
      ini_set('display_startup_errors', 1);
      ini_set('display_errors', 1);
      error_reporting(E_ALL);
+     */
  line after 1
  line after 2 
  line after 3
aviro
  • 5,532
  • IF I understand correctly, the patch file is recording the lines to change and make the path? If that is the case, then if I modify the original source code and add some lines above the "// Enable all errors" block, then that will fail? – alancc Mar 18 '24 at 04:41
  • @alancc no, look at my example, there are lines before and after the block. That's why I'm using context diff (-c), because it also records the lines before and after the changes, so it could know which block to change regardless if its location in the file. – aviro Mar 18 '24 at 08:21
  • OK. I see. Thank you. – alancc Mar 18 '24 at 11:23
3

Don't try to use sed for this as sed doesn't understand literal strings (see is-it-possible-to-escape-regex-metacharacters-reliably-with-sed), use a tool like awk that does understand literal strings.

Using GNU awk for multi-char RS and ARGIND:

$ awk -v RS='^$' -v ORS= '
    ARGIND < 3 { a[ARGIND]=$0; next }
    s = index($0,a[1]) {
        $0 = substr($0,1,s-1) a[2] substr($0,s+length(a[1]))
    }
    { print }
' old new file
this is
the winter
    /* For the production version, the following codelines are commented
       out
    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    */
of our
discontent

or using any awk:

$ awk '
    FNR == 1 { a[++argind]=$0; next }
    { a[argind] = a[argind] ORS $0 }
    END {
        $0 = a[3]
        if ( s = index($0,a[1]) ) {
            $0 = substr($0,1,s-1) a[2] substr($0,s+length(a[1]))
        }
        print
    }
' old new file
this is
the winter
    /* For the production version, the following codelines are commented
       out
    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    */
of our
discontent

The above were run using these input files:

$ head old new file
==> old <==
    //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);

==> new <== /* For the production version, the following codelines are commented out // Enable all errors ini_set('display_startup_errors', 1); ini_set('display_errors', 1); error_reporting(E_ALL); */

==> file <== this is the winter // Enable all errors ini_set('display_startup_errors', 1); ini_set('display_errors', 1); error_reporting(E_ALL); of our discontent

Ed Morton
  • 31,617
-1
 sed -e '1s/.*/\/* For the production version, the following codelines are commented\nout\n&/g'  -e '$s/.*/&\n*\//g'filename


output

/* For the production version, the following codelines are commented
out
  //  Enable all errors
    ini_set('display_startup_errors', 1);
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
*/