This question already has an answer here:

I have this output after executing diff:

< #R1#Number = Gauge32: 258     Name = STRING: "TATA"
> #R1#Number = Gauge32: 280     Name = STRING: "TATA"

I need to implement a sed command in a shell script to have this output:

Hostname=R1; old=258 new=280, Name="TATA"

marked as duplicate by roaima, Stephen Kitt, slm May 5 '15 at 15:09

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.


You could also try Perl instead:

diff file1 file2 | 
    perl -lne 'if(/^(.).*?#(.+?)#.*?:\s*(\d+).*(".+?")/){
        $1 eq ">" ? print "Hostname=$2; old=$o new=$3, Name=$4" : ( $o=$3 )}'


  • The -l adds a newline to each print call and the -n will cause the script given with -e to be applied to each line of the input file.
  • The regular expression will identify the first character (^.), the first string between # (#(.+?)#), a : followed by 0 or more whitespace and a set of numbers (:\s*(\d+)) and the last quoted string on the line (.*(".+?")). The parentheses cause these to be saved as $1 through $4.
  • foo ? bar : baz : this is a C construct present in many languages. It is shorthand for "if foo is true, then do bar, else do baz". So, if $1, the first character of the line which was saved by the previous match operation, is >, then we will print and if it isn't, we will save the 3rd pattern matched as $o.


diff file1 file2 | 
    perl -lane '$o = $F[4] if /^</; $F[1]=~s/#(.*)#.*/$1/; 
        print "Hostname=$F[1]; old=$o new=$F[4], Name=$F[8]" if />/'


  • The -a will cause Perl to automatically split each input line at whitespace and save it in the @F array.
  • $o = $F[4] if /^</; : this saves the 5th field as $o if the first character of the line is <.
  • $F[1]=~s/#(.*)#.*/$1/; : this will remove everything except the hostname from the second field ($F[1], arrays are numbered from 0).

Try this:

diff ... | sed -n -e '
    /^< / h
    /^---/ H
    /^> / { H; x; s/\n//g;
            s/^< #\([^#][^#]*\)#Number = Gauge32: \([0-9][0-9]*\) .* #\1#Number = Gauge32: \([0-9][0-9]*\) .* Name = STRING: \("[^"]*"\).*/Hostname=\1; old=\2 new=\3, Name=\4/;
            p; }'

The idea is to get everything on a single line, then pick up only the interesting stuff.

In detail:

  • sed -n - don't print anything by default
  • /^< / h - copy lines starting with < to the hold space
  • /^---/ H - append lines starting with --- to the hold space
  • /^> / { ... } - for lines starting with > do the following:
  • H - append the line to the hold space
  • x - exchange the hold space and the pattern space
  • s/\n//g - remove newlines; at this point the pattern space holds something like this: < #R1#Number = Gauge32: 258 Name = STRING: "TATA"---> #R1#Number = Gauge32: 280 Name = STRING: "TATA"
  • s/.../.../ - format the output (nothing clever here, just plain sed-fu)
  • p - print

This doesn't work for general diff output where changes can happen on adjacent lines. But it does answer the question the way you asked it.

Not the answer you're looking for? Browse other questions tagged or ask your own question.