share
Stack Overflowperl match consecutive newlines: `echo "aaa\n\n\nbbb" | perl -pe "s/\\n\\n/z/gm"`
[+2] [2] clay
[2023-01-27 03:31:22]
[ regex perl ]
[ https://stackoverflow.com/questions/75253819/perl-match-consecutive-newlines-echo-aaa-n-n-nbbb-perl-pe-s-n-n-z-gm ]

This works:

echo "aaa\n\n\nbbb" | perl -pe "s/\\n/z/gm"

aaazzzbbbz

This doesn't match anything:

echo "aaa\n\n\nbbb" | perl -pe "s/\\n\\n/z/gm"

aaa


bbb

How do I fix, so the regex matches two consecutive newlines?

Double-quotes are parsed by shell before perl sees the content. "\\n" means perl sees \n. - jhnc
Your first example doesn't do what you claim, although it will if you use single-quotes - jhnc
also, echo does not expand \n by default, so your second example doesn't output what you show either - jhnc
Using echo like this to test code is likely to create interpolation confusion. It is unlikely that this is the way you intend to use the code, so perhaps instead use it as file input, either inline with the ___DATA___ file handle, or by using a file with input, e.g. perl -pe "s....." testfile.txt - TLP
[+1] [2023-01-27 05:10:49] zdim [ACCEPTED]

A linefeed is matched by \n

echo "a\n\n\b" | perl -pe's/\n/z/'

This prints azzb, and without the following newline, so with the next prompt on the same line. Note that the program is fed one line at a time so there is no need for /g modifier. (And which is why \n\n doesn't match.) That /m modifier is then unrelated to this example.

I don't know in what form this is used but I'd imagine not with echo feeding the input? Then better test it with input in a file, or in a multi-line string (in which case /g may be needed).

An example

use warnings;
use strict;
use feature 'say';

# Test with multiline string  
my $ml_str = "a\n\nb\n";

$ml_str =~ s/\n/z/g;  #-->  azzbz (no newline at the end)
print $ml_str;    
say '';      # to terminate the line above

# Or to replace two consecutive newlines (everywhere)
$ml_str = "a\n\nb\n";   # restore the example string 
$ml_str =~ s/\n\n/z/g;  #-->  azb\n
print $ml_str;    


# To replace the consecutive newlines in a file read it into a string
my $file = join '', <DATA>;  # lines of data after __DATA__

$file =~ s/\n\n/z/g;
print $file;  

__DATA__
one
two


last

This prints

azzbz
azb
one
twoz
last


As a side note, I'd like to mention that with the modifier /s the . matches a newline as well. (For example, this is handy for matching substrings that may contain newlines by .* (or .+); without /s modifier that pattern stops at a newline.)

See perlrebackslash [1] and search for newline.


The /m modifier makes ^ and $ also match beginning and end of lines inside a multi-line string. Then

$multiline_string =~ s/$/z/mg;

will replace newlines inside the string. However, this example bears some complexities since some of the newlines stay.

[1] https://perldoc.perl.org/perlrebackslash

I don't want to match everything including new lines, I just want to match new lines. Also, what's wrong with using echo to pipe in input for a super simple test? - clay
@clay "don't want to match everything" -- yeah, obviously; I mentioned . just in one side comment at the very end (since it does match newline with /s). Nothing is wrong with using echo (I often do) but in this case there are issues with newlines (since echo interpretes them and thus sends separate lines to the program). That's all. I just think it's better to test this in a realistic setting - zdim
1
[0] [2023-01-27 05:29:18] ysth

You are applying substitution to only one line at a time, and one line will never have two newlines. Apply the substitution to the entire file instead:

perl -0777 -pe 's/\n\n/z/g'

This doesn't work: echo "aaa\n\nbbb" | perl -0777 -pe 's/\\n\\n/z/g' - clay
@clay Lose the double backslashes; just \n - zdim
oops, sorry, fixed - ysth
2