[SOLVED] Replace a string in nextline after searching a pattern

Hi,

I have a requirement where I need to replace a string in a line and this line will be identified by search criteria on previous line:
E.g.:
I have an xml file and Contents as below:

<Root>
        <NameValue>
            <name>Global/Text/Data</name>
            <value>This is valid data</value>
        </NameValue>
        <NameValueBoolean>
            <name>Global/Text/ID</name>
            <value>@%ID_Value</value>
        </NameValueBoolean>
        <NameValue>
            <name>Global/Text/Data</name>
            <value>This is not a valid data</value>
        </NameValue>
        <NameValuePassword>
            <name>Global/Text/ID</name>
            <value>@%ID_Value</value>
        </NameValuePassword>
        <NameValue>
            <name>Global/Text/Data</name>
            <value>This is SMS data</value>
        </NameValue>
        <NameValuePassword>
            <name>Global/Text/ID</name>
            <value>%&ID_Value</value>
        </NameValuePassword>
</Root>

Here, I have to seach for string contains ID in it and need to replace value in next line only if it has @%.

Can someone help in sorting this?

Thanks,
Krishna

please post your desired output.

not able to test below script. try

awk -v VM="New_value" -F "[<>]" '{if ($0 ~ /\/ID/) {s=1} else { if($0 ~ /@%/) {if(s=1){ sub(/@%ID_Value/,VM);s=0}}else{s=0}}}1' file
 awk '/ID</{print;getline;if(/@%/){sub(/ID_Value/,"CHANGE HERE");}} !/ID</' input_file

Whatever you wanna change just change in the text "CHANGE HERE".

Thanks for Quick responses
Please find the original and Desired Output details:

 
Original:
<Root>
<NameValueBoolean>
<name>Global/Text1/ID</name>
<value>@%ID_Value</value>
</NameValueBoolean>
<NameValuePassword>
<name>Global/Text2/ID</name>
<value>@%ID_Value</value>
</NameValuePassword>
<NameValuePassword>
<name>Global/Text3/ID</name>
<value>@%ID_Value</value>
</NameValuePassword>
<NameValuePassword>
<name>Global/Text4/ID</name>
<value>%&ID_Value</value>
</NameValuePassword>
</Root>

Desired Output:

<Root>
<NameValueBoolean>
<name>Global/Text1/ID</name>
<value>New Value</value>
</NameValueBoolean>
<NameValuePassword>
<name>Global/Text2/ABC</name>
<value>@%ID_Value</value>
</NameValuePassword>
<NameValuePassword>
<name>Global/Text3/ID</name>
<value>New Value</value>
</NameValuePassword>
<NameValuePassword>
<name>Global/Text4/ID</name>
<value>%&ID_Value</value>
</NameValuePassword>
</Root>

Thanks,
Krishna

Another way to do this is with ed:

#!/bin/ksh
ed -s in.xml <<-EOF
        gX<name>.*/ID<X.+1s/@%[^<]*/New Value/
        w out.xml
EOF

If you want to overwrite the input file instead of creating a new file, just change the 2nd line of the ed script from w out.xml to w .

I use ksh, but at least sh and bash will also work here.

Thanks for all your suggestions, but I am failing to full fill the requirements.

XML data is in a file and modified data should be updated to same file. only data having ">#!...<" needs to be changed if Element1, Element2 and user are available previous line. please find the original and output below:

Can some please help me to break this, thanks in advance.

Original:

<?xml version="1.0" encoding="UTF-8"?>
<application>
<repoInstanceName>%%DOMAIN%%-PrintFileProcessorBW</repoInstanceName>
<NVPairs>
<NameValuePairPassword>
<name>Message/Element1/MIG</name>
<value>#!YEkZA=</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element2/MIG</name>
<value>#!FQKi0=</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element3/MIG</name>
<value>#!FQKi0=</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element1/MIG</name>
<value>#!Lo44o=</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element2/MIG</name>
<value>#!HkCM=</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element3/MIG</name>
<value>#!FQKi0=</value>
</NameValuePairPassword>
</NVPairs>
<repoInstances selected="local">
<httpRepoInstance>
<user>ABC</user>
<password>#!JQLYU</password>
</httpRepoInstance>
<rvRepoInstance>
<user>ABC</user>
<password>#!WiRN4</password>
</rvRepoInstance>
</repoInstances>
</application>

Modified:

<?xml version="1.0" encoding="UTF-8"?>
<application>
<repoInstanceName>%%DOMAIN%%-PrintFileProcessorBW</repoInstanceName>
<NVPairs>
<NameValuePairPassword>
<name>Message/Element1/MIG</name>
<value>#!AAAAA</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element2/MIG</name>
<value>#!AAAAA</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element3/MIG</name>
<value>#!FQKi0=</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element1/MIG</name>
<value>#!AAAAA</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element2/MIG</name>
<value>#!AAAAA</value>
</NameValuePairPassword>
<NameValuePairPassword>
<name>Message/Element3/MIG</name>
<value>#!FQKi0=</value>
</NameValuePairPassword>
</NVPairs>
<repoInstances selected="local">
<httpRepoInstance>
<user>ABC</user>
<password>#!AAAAA</password>
</httpRepoInstance>
<rvRepoInstance>
<user>ABC</user>
<password>#!AAAAA</password>
</rvRepoInstance>
</repoInstances>
</application>

You can use this sed:

sed '/line 1 pattern/{
  N;
  /\nline 2 pattern/ {
    s/pattern/replace/
  }
}' file

please note when you use a "N" the pattern is in two line with \n separator. So you have to take care of your "s" command. And you have to know this sed doesn't work if you have a file with :

pattern1
pattern1
pattern2

For your last example you can do:

sed '/^<user>/{                                                  
N;
s/\n<password>.*</\n<password>#!AAAAA</
}' file

Try this..

 awk '{if ($0 ~ /Element1/ || $0 ~ /Element2/ || $0 ~ /<user>/) { print;getline;if(/ue>#!/) {$0 ="<value>#!AAAAA</value>"} else{if(/rd>#!/){$0="<password>#!AAAAA</password>"}}}}1' file

Thanks for all the suggestions; still one question remains in mind, does awk or sed commands updates same input file.

practically speaking no one....(this is as per my knowledge..:))

And this is always avoidable to perform reading and writing operation on the same file at the same time...:slight_smile:

You have to create one more file and mv it to original file..

perl -pi -e

writes to same file and it will allow to replace a string with other, but really not aware on how to place a condition before repalce.
If some option to search for a value and then to next line, it will be helpful.

It is never a good idea to edit your originals. What if there's a bug in your program? You've wrecked your input data! Make a new file and check, or at least back up first.

I agree with Corona688 that it is never a good idea to edit your originals without a backup.

I am also concerned that every post you make changes what you want to do. First we're changing a value tagged "@%ID_Value" in a line following a line that contains a name tagged string ending in "/ID" to an unspecified value. Then the new string to replace "@%ID_Value" is specified to be "New Value". And, now you want to change ">#!" followed by any characters up to the last "<" on the line to (no matter what tags are used on the line following a line that contains Element1, Element2, or user (no matter what tags appear on that line).

This sounds like a very dangerous set of changes to make since "user" could easily appear as a value in a data element in your XML file as well as being a tag, but the following ed script will do what you're asking for after saving a backup copy of your original file (assuming your original xml file is named in.xml . If your xml file has a different name, change in.xml in the following script to the name of the xml file you want to update. The backup copy of your file will have the name of your xml file with an underscore character prepended (e.g., _in.xml ):

#!/bin/ksh
file=in.xml
ed -s $file <<-EOF
        w _$file
        g/Element[12]/.+1s/>#!.*</>#!AAAAA</
        g/user/.+1s/>#!.*</>#!AAAAA</
        w
EOF

It worked, thanks to every one for your help and suggestions.

---------- Post updated at 05:59 PM ---------- Previous update was at 11:21 AM ----------

Hey would like to check one more thing. Can we have case sensitive check in ed as an additional requirement I can get first comparision element as "Element" or "ELEMENT" or "element"

Can some one throw some lite on it.

#!/bin/ksh
file=in.xml
ed -s $file <<-EOF
        w _$file
        g/[Ee][Ll][Ee][Mm][Ee][Nn][Tt][12]/.+1s/>#!.*</>#!AAAAA</
        g/user/.+1s/>#!.*</>#!AAAAA</
        w
EOF