1

I want to match multiple patterns in text files (in different directories) on the same line, copy the matched line to next line and modify the copied line. I want to duplicate every line that contains either the string "businessServices" or "BusinessServices". I don't care where the pattern to match appears in the line. I want to edit files in place. The desired added line are these global substitutions:

s#businessServices#userServices#g and s#validate#test#g
s#BusinessServices#UserServices#g
s#BUSINESS_SERVICES#USER_SERVICES#g

file1:

name="businessServices" value="validate"

file1 desired:

name="businessServices" value="validate"
name="userServices" value="test"

file2:

public static final String BUSINESS_SERVICES = "businessServices";

file2 desired:

public static final String BUSINESS_SERVICES = "businessServices";
public static final String USER_SERVICES = "userServices";

file3:

import com.my.ClientBusinessServicesRequest;
import com.my.ClientBusinessServicesResponse;

ClientBusinessServicesRequest clientBusinessServicesRequest = new ClientBusinessServicesRequest();
ClientBusinessServicesResponse clientBusinessServicesResponse = new ClientBusinessServicesResponse();

file3 desired:

import com.my.ClientBusinessServicesRequest;
import com.my.ClientBusinessServicesResponse;
import com.my.ClientUserServicesRequest;
import com.my.ClientUserServicesResponse;

ClientBusinessServicesRequest clientBusinessServicesRequest = new ClientBusinessServicesRequest();
ClientBusinessServicesResponse clientBusinessServicesResponse = new ClientBusinessServicesResponse();
ClientUserServicesRequest clientUserServicesRequest = new ClientUserServicesRequest();
ClientUserServicesResponse clientUserServicesResponse = new ClientUserServicesResponse();

file3 less desired (if file3 desired is too difficult to achieve):

import com.my.ClientBusinessServicesRequest;
import com.my.ClientUserServicesRequest;
import com.my.ClientBusinessServicesResponse;
import com.my.ClientUserServicesResponse;

ClientBusinessServicesRequest clientBusinessServicesRequest = new ClientBusinessServicesRequest();
ClientUserServicesRequest clientUserServicesRequest = new ClientUserServicesRequest();
ClientBusinessServicesResponse clientBusinessServicesResponse = new ClientBusinessServicesResponse();
ClientUserServicesResponse clientUserServicesResponse = new ClientUserServicesResponse();

I tried the following, each one get some desired results but not the entire desired results.

grep -rl businessServices . | xargs sed -i 's#\(.*\)validate\(.*\)#&\n\1test\2#' 

grep -rl businessServices . | xargs sed -i 's#\(.*\)businessServices\(.*\)#&\n\1userServices\2#'

grep -rl BusinessServices . | xargs sed -i 's#\(.*\)BusinessServices\(.*\)#&\n\1UserServices\2#g'

grep -rli businessServices . | xargs sed -i 's#\(.*\)BUSINESS_SERVICES\(.*\)#&\n\1USER_SERVICES\2#' 
7
  • Do you want to only duplicate (and modify) lines that match some pattern? For instance, do you want to duplicate every line that contains the string "validate" (and replace "validate" with "test" in the second line)? You don't care where "validate" appears in the line?
    – aviro
    Commented Dec 23, 2021 at 20:50
  • Also posted at [community.unix.com/t/…
    – RudiC
    Commented Dec 23, 2021 at 21:27
  • Thanks for your comments. I edited to clarify my post.
    – albertkao9
    Commented Dec 23, 2021 at 22:21
  • Regarding I want to edit files in place - if that's because you don't have enough disk space to use a temp file then please let us know because all Unix utilities (except ed which uses an internal buffer) e.g. sed, awk, perl, ruby, etc. use a temp file behind the scenes to do what they refer to as "inplace" editing and so that would greatly complicate your problem. You can of course force some of those tools to read the whole file into memory as ed does and then write it out at the end but if your file is too massive to be copied on disk it may be too big to fit in memory too.
    – Ed Morton
    Commented Dec 24, 2021 at 8:47
  • 1
    cmd file > tmp && mv tmp file is trivial using any command, the "inplace" editing stuff is just syntactic sugar as that's what they all do anyway. Yes, 1 input/output is easier for us to copy/paste to test with than 3 pairs of files BUT if you want to work with multiple input files it's good to include 2 (or 3) in your question so it's fine as-is. Regarding the 2 different "file3" outputs - if you need the "desired" one then don't include an undesirable version.
    – Ed Morton
    Commented Dec 25, 2021 at 13:12

3 Answers 3

2

Using GNU sed we and in extended regex mode (-E), with no autoprint (-n), and in place editing (-i)

sed -Eni '
  /[bB]usinessServices|BUSINESS_SERVICES/{
    h
    :loop
      ${g;bend;}
      n
    //{H;bloop;}
    x
    :end
    p'"
    $(< cmds.sed)
    "'
    $!G
  }
  p
' file3

  • Use the OR regex to select the desired line.
  • Then begin a loop to store the consecutive desired lines in the hold
  • print the bunch of consecutive line(s) stored in hold.
  • then perform the substitution operation by interpolating the sed commands stored in a file (cmds.sed)
  • print the modified bunch + anything in hold.

Contents of cmds.sed file:

$ cat cmds.sed

s#businessServices#userServices#g

s#validate#test#g

s#BusinessServices#UserServices#g

s#BUSINESS_SERVICES#USER_SERVICES#g
1
  • guest_7, Thanks for your answer and detailed explanation!!! I tried and got the file3 desired. Great!!!
    – albertkao9
    Commented Dec 25, 2021 at 2:38
1

Using GNU awk for "inplace" editing and ENDFILE, just buffer the changed lines until you hit a blank line or end of file and then print them:

$ awk -i inplace '
    !NF {
        printf "%s", buf
        buf = ""
    }
    {
        print
        orig = $0
        gsub(/businessServices/,"userServices")
        gsub(/validate/,"test")
        gsub(/BusinessServices/,"UserServices")
        gsub(/BUSINESS_SERVICES/,"USER_SERVICES")
    }
    $0 != orig {
        buf = buf $0 ORS
    }
    ENDFILE {
        printf "%s", buf
        buf = ""
    }
' file1 file2 file3

$ head file1 file2 file3
==> file1 <==
name="businessServices" value="validate"
name="userServices" value="test"

==> file2 <==
public static final String BUSINESS_SERVICES = "businessServices";
public static final String USER_SERVICES = "userServices";

==> file3 <==
import com.my.ClientBusinessServicesRequest;
import com.my.ClientBusinessServicesResponse;
import com.my.ClientUserServicesRequest;
import com.my.ClientUserServicesResponse;

ClientBusinessServicesRequest clientBusinessServicesRequest = new ClientBusinessServicesRequest();
ClientBusinessServicesResponse clientBusinessServicesResponse = new ClientBusinessServicesResponse();
ClientUserServicesRequest clientUserServicesRequest = new ClientUserServicesRequest();
ClientUserServicesResponse clientUserServicesResponse = new ClientUserServicesResponse();
4
  • Ed Morton, Thanks for your answer!!! I tried and got the file3 less desired. Good! How to get the file3 desired?
    – albertkao9
    Commented Dec 25, 2021 at 2:39
  • You're welcome. I updated my answer to show how to get the file3 desired changes and change all of your input files at once.
    – Ed Morton
    Commented Dec 25, 2021 at 13:35
  • I know only a little bit of awk. Would you mind explaining your awk script, please? In particular, about these: 1. Extra codes are removed for clarity. { orig = $0 } "$0 != orig" 2. Does "!NF" mean not the last field in the current record?
    – albertkao9
    Commented Dec 27, 2021 at 14:39
  • 1
    1) That's saving $0 so we can test it later to see if the gsub()s made any changes. 2) No it means NF (the count of the number of fields on the current input line) is not 0, i.e. the line contains some non-space characters.
    – Ed Morton
    Commented Dec 27, 2021 at 14:42
1

I think this does what you want with two sed:

$ sed -e 's/.*business.*\|.*Business.*/&\n&/' \
    -e 's/business/user/g2' \
    -e 's/BUSINESS/USER/2' \
    -e '/^import com.my.ClientBusinessServicesResponse/s/Business/User/2' \
    -e '/^ClientBusinessServicesResponse/s/Business/User/4g' file1 file2 file3

# file1
name="businessServices" value="validate"
name="userServices" value="validate"

# file2
public static final String BUSINESS_SERVICES = "businessServices";
public static final String USER_SERVICES = "userServices";

# file3
import com.my.ClientBusinessServicesResponse;
import com.my.ClientUserServicesResponse;

ClientBusinessServicesResponse clientBusinessServicesResponse = new ClientBusinessServicesResponse();
ClientUserServicesResponse clientUserServicesResponse = new ClientUserServicesResponse();

Finally

$ sed '/userServices/s/validate/test/' file1
1
  • schrodigerscatcuriosity, Thanks for your answer!!! I tried and got the file3 less desired. Good! How to get the file3 desired (which I edited recently)?
    – albertkao9
    Commented Dec 23, 2021 at 23:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .