Replace text in a file and add numbering to the new string - perl

I want to replace every occurrence of a string in a file but in a way that the new text will be numbered. Example:
from:
foo bar a
foo bar b
foo bar c
to:
bar baz1 a
bar baz2 b
bar baz3 c
How to do this in command line?

One way with awk:
$ awk '{gsub(/foo bar/,"bar baz"++i)}1' file
bar baz1 a
bar baz2 b
bar baz3 c

perl -lape '#F[0,1] = ($F[1], "baz". ++$i); $_= "#F"' file

Using shell.
#! /usr/bin/bash
i=0
while read a b c
do
((i++))
echo "bar baz$i $c"
done < file

Related

How to parse rows in my txt file properly using perl

I hope to parse a txt file that looks like this:
A a, b, c
B e
C f, g
The format I hope to get is:
A a
A b
A c
B e
C f
C g
I tried this:
perl -ane '#s=split(/\,/, $F[1]); foreach $k (#s){print "$F[0] $k\n";}' txt.txt
but it only works when there's no space after commas. In the original file, there is a space after each comma. What should I do?
$ perl -lane 'print "$F[0] $_" for map { tr/,//rd } #F[1..$#F]' input.txt
A a
A b
A c
B e
C f
C g
Use auto-split mode on whitespace like normal, and for each element of an array slice of #F from the second field to the last one, remove any commas (I used tr//d, the more usual s/// works too, of course) and print it with the first field prepended.
Alternatively, don't use -a because it splits too much.
perl -le'#F = split(" ", $_, 2); print "$F[0] $_" for split(/,\s*/, $F[1])'

Extract text with different delimiters

my textfile looks like this
foo.en 14 :: xyz 1;foo bar 2;foofoo 5;bar 9
bar.es 18 :: foo bar 4;kjp bar 2;bar 6;barbar 8
Ignoring text before the :: delimiter, is there a one liner unix command (many pipes allowed) or one liner perl script that extract the text such that yields the output of unique words delimited by ; ?:
xyz
foo bar
foofoo
bar
kjp bar
barbar
i've tried looping through the textfile with a python script but i'm looking for a one-liner for the task.
ans = set()
for line in open(textfile):
ans.add(line.partition(" :: ")[1].split(";").split(" ")[:-1])
for a in ans:
print a
With Perl:
perl -nle 's/.*?::\s*//;!$s{$_}++ and print for split /\s*\d+;?/' input
Description:
s/.*?::\s*//; # delete up to the first '::'
This part:
!$s{$_}++ and print for split /\s*\d+;?/
can be rewritten like this:
foreach my $word (split /\s*\d+;?/) { # for split /\s*\d+;?/
if (not defined $seen{$word}}) { # !$s{$_}
print $word; # and print
}
$seen{$word}++; # $s{$_}++
}
Since the increment in !$s{$_}++ is a post increment, Perl first test for the false condition and then does the increment. An undefined hash value has the value 0. If the test fails, i.e., $s{$_} was previously incremented, then the and part is skipped due to short circuiting.
cat textfile | sed 's/.*:://g' | tr '[0-9]*;' '\n' | sort -u
Explanation:
sed 's/.*:://g' Take everything up to and including `::` and replace it with nothing
tr '[0-9];' '\n' Replace numbers and semicolon with newlines
sort -u Sort, and return unique instances
it does result in a sorted output, I believe...
You can try this:
$ awk -F ' :: ' '{print $2}' input.txt | grep -oP '[^0-9;]+' | sort -u
bar
barbar
foo bar
foofoo
kjp bar
xyz
If your phrases contains numbers, try this perl regex: '[^;]+?(?=\s+\d+(;|$))'
With only awk :
$ awk -F' :: ' '{
gsub(/[0-9]+/, "")
split($2, arr, /;/ )
for (a in arr) arr2[arr[a]]=""
}
END{
for (i in arr2) print i
}' textfile.txt
And a one-liner version :
awk -F' :: ' '{gsub(/[0-9]+/, "");split($2, arr, /;/ );for (a in arr) arr2[arr[a]]="";}END{for (i in arr2) print i}' textfile.txt

Can I move the debugger context up and down the stack?

Basically I'm looking for the perl equivalent of gdb's "up" and "down" commands. If I break on subroutine bar, and I have a call stack that looks like this:
foo
\
baz
\
bar
I'd like to be able to (without returning from bar or baz) navigate up the foo frame and see what it was doing by manipulating variables as I ordinarily would using arguments to p or x.
Use the y command.
$ cat frames.pl
sub bar {
my $fnord = 42;
1
}
sub baz { bar }
sub foo {
my $fnord = 23;
baz
};
foo;
$ perl -d frames.pl
Loading DB routines from perl5db.pl version 1.37
Editor support available.
Enter h or 'h h' for help, or 'man perldebug' for more help.
main::(frames.pl:10): foo;
DB<1> c 3
main::bar(frames.pl:3): 1
DB<2> y 2 fnord
$fnord = 23
DB<3> T
. = main::bar() called from file 'frames.pl' line 5
. = main::baz() called from file 'frames.pl' line 8
. = main::foo() called from file 'frames.pl' line 10

Perl Search and Capture Line After

Alright, so this is probably a pretty easy question to answer, but I'm struggling with it. I'm trying to get my program to capture everything after a certain word and then print it. For example, if the input text is
bar foo foo foo foo
Then I want the output to be "foo foo foo foo" if I am searching for bar. I hope my question makes sense. Any help would be greatly appreciated! I'm very new to perl, so the more explanation you can give, the better. Thank you!
#! /usr/bin/env perl
*ARGV = *DATA; # for demo only
while (<>) {
print "line $.: $1\n" if /bar\s+(.+)/;
}
__DATA__
you can't see me
bar foo foo foo foo
nope
Output:
line 2: foo foo foo foo

sed/awk: replace N occurrence

it's possible to change N (for example second occurrence) in file using one-line sed/awk except such method?:
line_num=`awk '/WHAT_TO_CHANGE/ {c++; if (c>=2) {c=NR;exit}}END {print c}' INPUT_FILE` && sed "$line_num,$ s/WHAT_TO_CHANGE/REPLACE_TO/g" INPUT_FILE > OUTPUT_FILE
Thanks
To change the Nth occurence in a line you can use this:
$ echo foo bar foo bar foo bar foo bar | sed 's/foo/FOO/2'
foo bar FOO bar foo bar foo bar
So all you have to do is to create a "one-liner" of your text, e.g. using tr
tr '\n' ';'
do your replacement and then convert it back to a multiline again using
tr ';' '\n'
This awk solution assumes that WHAT_TO_CHANGE occurs only once per line. The following replaces the second 'one' with 'TWO':
awk -v n=2 '/one/ { if (++count == n) sub(/one/, "TWO"); } 1' file.txt