Reputation: 1032
I have this string as follows(which is the content of the test1.txt file):
one
1
</>
two
2
</>
I want it to become a new string like this:
one
1
</>
1
one
</>
two
2
</>
2
two
</>
I use the following perl oneliner to do just that.
perl -pi.bak -e 's#((.*)\n(.*)\n<\/>)#$1\n$3\n$2\n<\/>#g' "test1.txt"
but it did nothing to the test1.txt file.
Update:I like all three answers. They all provide some brilliantly useful information. I'm not sure in this situation which answer to accept…
Upvotes: 1
Views: 318
Reputation: 385506
You are reading a line at a time and matching against that one line, so your pattern can't possibly match.
The simple solution is to read the entire file as one line by using -0777
(which sets $/
to undef
).
perl -i.bak -0777pe's#((.*)\n(.*)\n<\/>)#$1\n$3\n$2\n<\/>#g' test1.txt
Upvotes: 3
Reputation: 106445
The -p
option assigns the input to the $_
variable on a per-line basis, so your regex, which matches multiple lines, would not find a match. You should read the entire file before you try to apply the regex instead:
perl -i.bak -e 'undef $/;$_=<>;s#((.*)\n(.*)\n</>)#$1\n$3\n$2\n</>#g;print' "test1.txt"
Sample run in command line:
# perl -e 'undef $/;$_=<>;s#((.*)\n(.*)\n</>)#$1\n$3\n$2\n</>#g;print'<<EOF
> one
> 1
> </>
> two
> 2
> </>
> EOF
one
1
</>
1
one
</>
two
2
</>
2
two
</>
Upvotes: 2
Reputation: 5952
Di-section of your one liner:
$ perl -MO=Deparse -pi.bak -e 's#((.*)\n(.*)\n<\/>)#$1\n$3\n$2\n<\/>#g' test.txt
BEGIN { $^I = ".bak"; }
LINE: while (defined($_ = readline ARGV)) {
s[((.*)\n(.*)\n<\/>)][$1\n$3\n$2\n</>]g;
}
continue {
die "-p destination: $!\n" unless print $_;
}
-e syntax OK
i.e. your processing loop is line-based, whereas your regex wants to match multiple lines.
NOTE: my solutions use the more generic filter approach STDIN to STDOUT, not -i.bak
.
You'll either have to slurp the file to memory and then apply the substitution...
#!/usr/bin/perl
use warnings;
use strict;
use open qw(:encoding(UTF-8) :std);
my $input;
{
local $/;
$input = <STDIN>;
}
$input =~ s,((.*)\n(.*)\n<\/>),$1\n$3\n$2\n<\/>,g;
print $input;
exit 0;
... or use section detection with bi-stable range operator in scalar context:
#!/usr/bin/perl
use warnings;
use strict;
use open qw(:encoding(UTF-8) :std);
my @section;
while (<STDIN>) {
if (/^\w+$/../^<\/>$/) {
push(@section, $_);
}
print;
# End of section reached
if (/^<\/>$/) {
# swivel lines around for desired output result...
print @section[1, 0, 2];
@section = ();
}
}
exit 0;
Which approach is more appropriate depends on your real-life input file or additional processing requirements.
Test run:
$ perl dummy.pl <dummy.txt
one
1
</>
1
one
</>
two
2
</>
2
two
</>
UPDATE if "no redirection" is an absolute requirement, you can replace the <STDIN>
with <>
to process the files on the command line, i.e.
my $input = <>;
or
while (<>) {
and on the command line:
$ perl -i.bak dummy.pl test1.txt
Upvotes: 2