2

I have lines form a command output as given below.

service:clus1-svr          clus1-node2                      started    [Z]
service:clus1-svr          clus1-node2                      started

I want to add string before and after the line which indicate [Z] only. So the result may come as:

<font>service:clus1-svr          clus1-node2                      started    [Z]</font>

Can anyone please help me to do this. Your help greatly appreciated.

i tried below, but that changes all the line from the output

sed -i "s/.*/<font color="red">&<\/font>/"
1
  • Are the separators spaces or tabs?
    – kos
    Sep 3, 2015 at 21:31

3 Answers 3

4

You can use this :

sed -n '/\[Z\]$/ s/^/<font>/;/\[Z\]$/ s/$/<\/font>/p' file.txt

As you will use it over STDOUT of another command, so use it as e.g. :

some command | sed -n '/\[Z\]$/ s/^/<font>/;/\[Z\]$/ s/$/<\/font>/p'

It basically has two parts :

  • /\[Z\]$/ s/^/<font>/ will match the line that has [Z] at last, then will add <font> at the start of the line by s/^/<font>/

  • /\[Z\]$/ s/$/<\/font>/ will match [Z] at the end of the line and then will add </font> at the end of the line using /$/<\/font>/.

Example :

$ cat file.txt 
service:clus1-svr          clus1-node2                      started    [Z]
service:clus1-svr          clus1-node2                      started

$ sed -n '/\[Z\]$/ s/^/<font>/;/\[Z\]$/ s/$/<\/font>/p' file.txt 
<font>service:clus1-svr          clus1-node2                      started    [Z]</font>

As @glennjackman has pointed out you can also use a grouping construct to do all the replacements for a single match :

$ sed -n '/\[Z\]$/ { s/^/<font>/; s/$/<\/font>/p }' file.txt 
<font>service:clus1-svr          clus1-node2                      started    [Z]</font>

How about a bash solution :

while IFS= read -r line; do [[ $line =~ \[Z\]$ ]] && echo "<font>${line}</font>"; done <file.txt

You can use it as :

some command | while IFS= read -r line; do [[ $line =~ \[Z\]$ ]] && echo "<font>${line}</font>"; done

This uses the same logic as the sed one. If the line ends with [Z] ([[ $line =~ \[Z\]$ ]]), then the line is printed in desired format.

Example :

$ while IFS= read -r line; do [[ $line =~ \[Z\]$ ]] && echo "<font>${line}</font>"; done <file.txt 
<font>service:clus1-svr          clus1-node2                      started    [Z]</font>
8
  • 1
    sed has a grouping construct, so you can write sed '/\[Z\]/ { s/^/<font>; s,$,</font>, } Sep 3, 2015 at 21:25
  • @glennjackman Thanks..AFAIK thats a GNU entension, right ?
    – heemayl
    Sep 3, 2015 at 21:30
  • Also I think OP were close with their attempt: sed -i '/\[Z\]$/ s/.*/<font>&<\/font>/' infile
    – kos
    Sep 3, 2015 at 21:30
  • @kos Yeah, your modification is on the money....
    – heemayl
    Sep 3, 2015 at 21:33
  • 1
    @kos done.... :)
    – heemayl
    Sep 3, 2015 at 21:57
2

You were pretty close with your attempt; all you needed to do was to narrow down the execution of the substitution only to lines containing [Z] at the end:

sed -i '/\[Z\]$/ s/.*/<font>&<\/font>/' infile

However, here's another awk solution:

awk '$4=="[Z]" {printf("%s%s%s\n", "<font>", $0, "</font>"); next} {print}' infile
  • $4=="[Z]": pattern; if the 4th field of the currently processed record is [Z], executes the following action; otherwise, it skips to the next pattern / action;
  • printf("%s%s%s\n", "<font>", $0, "</font>"): prints <font>, the currently processed record and </font> as strings followed by a newline;
  • next: skips to the next record;
  • print: prints the currently processed record followed by a newline;

Sample output:

user@debian ~ % cat infile
service:clus1-svr          clus1-node2                      started    [Z]
service:clus1-svr          clus1-node2                      started
user@debian ~ % awk '$4=="[Z]" {printf("%s%s%s\n", "<font>", $0, "</font>"); next} {print}' infile
<font>service:clus1-svr          clus1-node2                      started    [Z]</font>
service:clus1-svr          clus1-node2                      started
1
  • Thank you very much Kos, sed -i '/[Z]$/ s/.*/<font>&<\/font>/' infile resolved the query i have in a pretty cool manner. I have also tried awk '$4=="[Z]" {printf("%s%s%s\n", "<font>", $0, "</font>"); next} {print}' infile. Awsome, i am happy with this too. Thank you very very much
    – Rio
    Sep 5, 2015 at 8:00
1

Awk version.

Tested with the OP's example pasted twice into the same file, so as you can see it edits those two lines which have [Z], and two which don't contain it

  $ awk ' {if ($0~/\[Z\]/)  print "<font>"$0"</font>";else print}' testfile.txt                                                                   
<font>service:clus1-svr          clus1-node2                      started    [Z]</font>
service:clus1-svr          clus1-node2                      started
<font>service:clus1-svr          clus1-node2                      started    [Z]</font>
service:clus1-svr          clus1-node2                      started

To output the edits to the file, use the following construct:

awk ' {if ($0~/\[Z\]/) print "<font>"$0"</font>";else print}' testfile.txt > /tmp/tmp.txt && cat /tmp/tmp.txt > testfile.txt

2
  • I like this solution more than my solution actually. I forgot that you can concatenate strings that way with print
    – kos
    Sep 3, 2015 at 22:00
  • Yes. This is absolutely great. Thank you Serg. I am in trouble as i am not able to select you all as winner :( Yes you guys rocks!
    – Rio
    Sep 5, 2015 at 8:13

You must log in to answer this question.

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