Perl regex: replace all \n to "|", Except a \n followed by some static text(abc)

Viewed 138

I am having a file f1.txt which contains below text

abc
123
456
abc
456
343
abc
asd
23d

I want output of my regex to be

Every abc should start with new line, with all else (should be separated by | symbol) up to the next abc on it

abc|123|456
abc|456|343
abc|asd|23d

I tried below regex which is not producing expected result.

perl -p -e 's/\n(?!abc)/|/g' f1.txt 

What will be the correct regex?, what i am missing? Thanks

2 Answers

You may use this perl command in slurp mode:

perl -0777 -pe 's/(\S+)\R(?!abc\R|\z)/$1|/g' file
abc|123|456
abc|456|343
abc|asd|23d

or else:

perl -0777 -pe 's/(\S+)\K\R(?!abc\R|\z)/|/g' file

RegEx Details:

  • (\S+): Match 1+ non-whitespace character
  • \R: Match a line break
  • (?!abc\R|\z): Negative lookahead to assert we don't have abc followed by line break OR end of file ahead

Replacement

  • /$1|: Put captured value in group #1 back with |

An awk solution:

awk '{printf "%s", ($1 ~ /^abc/ ? (NR>1?"\n":"") : "|") $0} END{print ""}' file

You don't need a regex or to slurp the entire file. If you instead think about what you have to output before each element instead of after each element, you don't have to lookahead. You are outputting a pipe character before every element unless that element is the magic string. When you see that magic string, you output a newline before it. You only ever need to know the current element to know what you need to do. The only time you don't output a prefix is the first line.

Here's a one-liner that will do the job. This reads each line and removes the newline with chomp, so that part of the problem is solved. Next, it figures out what should be in front of the thing it's about to output. It precedes the element with a | if the string is not the first column magic text, and a newline otherwise (except if it's the first line you've read):

$ perl -ne 'chomp; print $_ eq q(abc) ? ($.==1 ? q() : qq(\n) ) . $_ : qq(|$_)' in.txt

The embedded conditional operator is a bit messy, and is essentially this program:

while( <DATA> ) {
    chomp;
    print do {
        if( $_ eq 'abc' ) {
            if( $. == 1 ) { $_ }
            else { "\n$_" }
            }
        else { "|$_" }
        }
    }
Related