sed to replace in a file, with both old and new strings in files

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?

Asked By: alancc

||

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
Answered By: aviro

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
Answered By: Ed Morton
 sed -e '1s/.*//* For the production version, the following codelines are commentednoutn&/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);
*/
Answered By: Praveen Kumar BS
Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.