Search code examples
sedreplacezsh

sed: replace a big block of code by another on a large number of files if the code block exists in the inspected files


I have the following block of code:

# Block bias spectro + pshot and correlations
FISH_super_flat_GCsp[0:7,0:7] = FISH_EUCLID_GCsp_flat[0:7,0:7]
FISH_super_flat_GCsp[7:17,7:17] = FISH_EUCLID_GCsp_flat[7:17,7:17]
FISH_super_flat_GCsp[0:7,7:17] = FISH_EUCLID_GCsp_flat[0:7,7:17]
FISH_super_flat_GCsp[7:17,0:7] = FISH_EUCLID_GCsp_flat[7:17,0:7]
# Block bias photo and correlations
FISH_super_flat_XC[0:7,0:7] = FISH_EUCLID_XC_flat[0:7,0:7]
FISH_super_flat_XC[20:31,20:31] = FISH_EUCLID_XC_flat[10:21,10:21]
FISH_super_flat_XC[0:7,20:31] = FISH_EUCLID_XC_flat[0:7,10:21]
FISH_super_flat_XC[20:31,0:7] = FISH_EUCLID_XC_flat[10:21,0:7]
# Block I.A and correlations
FISH_super_flat_XC[17:20,17:20] = FISH_EUCLID_XC_flat[7:10,7:10]
FISH_super_flat_XC[0:7,17:20] = FISH_EUCLID_XC_flat[0:7,7:10]

I would like to replace it, if it exists in files, in a large number of Python files by this new block:

# Block bias spectro + pshot and correlations
FISH_super_flat_GCsp[0:7,0:7] = FISH_EUCLID_GCsp_flat[0:7,0:7]
FISH_super_flat_GCsp[7:17,7:17] = FISH_EUCLID_GCsp_flat[7:17,7:17]
FISH_super_flat_GCsp[0:7,7:17] = FISH_EUCLID_GCsp_flat[0:7,7:17]
FISH_super_flat_GCsp[7:17,0:7] = FISH_EUCLID_GCsp_flat[7:17,0:7]
# Block bias photo and correlations
FISH_super_flat_XC[0:7,0:7] = FISH_EUCLID_XC_flat[0:7,0:7]
FISH_super_flat_XC[17:31,17:31] = FISH_EUCLID_XC_flat[7:21,7:21]
FISH_super_flat_XC[0:7,17:31] = FISH_EUCLID_XC_flat[0:7,7:21]
FISH_super_flat_XC[17:31,0:7] = FISH_EUCLID_XC_flat[7:21,0:7]

I know that it may be possible by using sed (or surely gsed since I am on zsh MacOS 11.3) but I need help since I have only one shot possible (that's why I am going to make a backup of the director containing all the Python files to inspect).

I may use a command of kind:

find directory_to_replace/ -type f -name '*.py' -exec sed **UNKNOWN COMMAND** {} \;

I know to replace a word or regular expression with sed (like sed -i 's/\(pattern1\)/pattern2/g ) but I have no idea how to manage to replace big blocks of code like above.

Maybe I need to put these two blocks into text files and handle with sed the replacement but this is the first time that I need to do this kind of operation.

Update

Given the solution done by @potong, I have put this command in a script and include the command in a loop over all Python files :

#!/bin/bash

for i in $(find . -type f -name '*.py'); do
echo $i
gsed -e '$p;e echo MATCH;cat matchFile;echo REPLACE;cat replaceFile' -e '$d' $i |\
gsed -Ez '':a;s/(.*)(.*\nMATCH\1REPLACE(.*))$/\3\2/;ta;s/MATCH\n.*//'
done

As you can see, I used GNU sed "gsed" since I am on MacOS 11.6 :

Unfortunately, I get the following error:

./script_replacement_block_synthesis.sh: line 6: syntax error near unexpected token `.*'
./script_replacement_block_synthesis.sh: line 6: `gsed -Ez '':a;s/(.*)(.*\nMATCH\1REPLACE(.*))$/\3\2/;ta;s/MATCH\n.*//''

How can I fix this error?


Solution

  • This might work for you (GNU sed):

    sed -e '$p;e echo MATCH;cat matchFile;echo REPLACE;cat replaceFile' -e '$d' file |
    sed -Ez ':a;s/(.*)(.*\nMATCH\1REPLACE(.*))$/\3\2/;ta;s/(.*)MATCH\n.*/\1/'
    

    Place the contents of the match in matchFile and the replacement in replaceFile.

    Append the match and replacement to the file in question and pipe the result through to another sed invocation.

    Using pattern matching, match the matchFile with the match in the file and replace it by the replaceFile and repeat until failure.

    Finally remove the match and replace files from the end of the original file.

    Alternative:

    cat file <(echo MATCH) matchFile <(echo REPLACE) replaceFile |
    sed -zE ':a;s/(.*)(.*MATCH\1REPLACE(.*))$/\3\2/;ta;s/(.*)MATCH.*/\1/'
    

    Or:

    cat file <(echo MATCH) matchFile <(echo REPLACE) replaceFile |
    sed -E 'H;1h;$!d;x;:a;s/(.*)(.*MATCH\1REPLACE(.*))$/\3\2/;ta;s/(.*)MATCH.*/\1/'