Update particular tag in a XML file

Below is the content in my XML file

<name>XXX</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>
<name>YYY</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>
<name>ZZZ</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>

I want to replace "Delay" as "Down" under name "XXX" only.

Is it possible to update using awk function. Please let me know the command.

Thanks,
Viswa

Any attempts/ideas/thoughts from your side?

sed -i "s/Delay/Down/g" filename

Above replaces all "Delay" to "Down"

I want to apply this under " <name>XXX</name> "

Thanks,
Viswa

Parsing XML is not trivial. If I have to do it in shell, I usually use my yanx.awk script

$ cat upd.awk

BEGIN { ORS="" }
TAG=="NAME" {
        name=$2;
        gsub(/(^[ \r\n\t]+)|([\r\n\t ]+$)/, "", name);
}

(name=="XXX") && (TAG=="EVENTTYPE") && /Delay/ { $2="Down"; }

{        printf("<%s",$0);}

$ awk -f yanx.awk -f upd.awk input.xml

<name>XXX</name>
         <eventType>Uptime</eventType>
         <eventType>Down</eventType>
         <eventType>Down</eventType>
<name>YYY</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>
<name>ZZZ</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>

$

While Corona668's xml-parser is a powerful tool, it may seem oversized for this small problem. Try

awk 'match ($0, /^<name>[^<]*/) {XF = substr ($0, RSTART+6, RLENGTH-6) == "XXX"} XF {sub (/Delay/, "Down")} 1 ' file4
<name>XXX</name>
         <eventType>Uptime</eventType>
         <eventType>Down</eventType>
         <eventType>Down</eventType>
<name>YYY</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>
<name>ZZZ</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>
perl -pe 'BEGIN{$/="<name>"} if(/XXX<\/name/){s/Delay/Down/g}' file.xml

Output:

<name>XXX</name>
         <eventType>Uptime</eventType>
         <eventType>Down</eventType>
         <eventType>Down</eventType>
<name>YYY</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>
<name>ZZZ</name>
         <eventType>Uptime</eventType>
         <eventType>Delay</eventType>
         <eventType>Delay</eventType>

All the above commands works fine but it just print as "down" in the screen, I want to update the value in file "file.xml"

Thanks,
Viswa

---------- Post updated at 12:58 AM ---------- Previous update was at 12:37 AM ----------

perl -pe 'BEGIN{$/="<name>"} if(/XXX<\/name/){s/Delay/Down/g}' file.xml >> file.xml

If I use the above command it appends the updated value to file.xml, I want to overwrite the content.

Thanks,
Viswa

You seem to have at least heard of redirections, but >> is MEANT to append to files, so above described behaviour is no surprise. Don't use the single > to the input file either as it would destroy the target and thus the input before it can be read.
Try > TMP && mv TMP file (which might lose the original file's attributes).

1 Like

If the command:

perl -pe 'BEGIN{$/="<name>"} if(/XXX<\/name/){s/Delay/Down/g}' file.xml

produces the data you want to appear in your file on the screen, and there are one or more hard links to file.xml , the following should do what you want safely:

perl -pe 'BEGIN{$/="<name>"} if(/XXX<\/name/){s/Delay/Down/g}' file.xml > file.xml$$ && cp file.xml$$ file.xml && rm file.xml$$

If you are absolutely positive that there will never be more than one hard link to file.xml and you don't care about preserving modes and ownership of the original file, you can change:

cp file.xml$$ file.xml && rm file.xml$$

in the above to:

mv file.xml$$ file.xml

to make it run a little bit faster, and avoid the period of time while the file is being copied where someone reading file.xml might find an incomplete file.

1 Like

Just add the highlighted -i flag. Do not include the >> file.xml part.

perl -i -pe 'BEGIN{$/="<name>"} if(/XXX<\/name/){s/Delay/Down/g}' file.xml
1 Like

Works fine.

Thank you so much

Viswa

@viswanath:

Please try to use single redirection symbol in your code like below.

perl -pe 'BEGIN{$/="<name>"} if(/XXX<\/name/){s/Delay/Down/g}' file.xml > file.xml

NO! Do not do this. The first thing that the above command does is reduce file.xml to length zero. The perl command then reads that zero length file and, finding no data to process, leaves you with all of your original data deleted.