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
Related
According to perldoc, String Eval should be performed in the current scope. But the following simple test seems to contradict this.
We need the following two simple files to set up the test. Please put them under the same folder.
test_eval_scope.pm
package test_eval_scope;
use strict;
use warnings;
my %h = (a=>'b');
sub f1 {
eval 'print %h, "\n"';
# print %h, "\n"; # this would work
# my $dummy = \%h; # adding this would also work
}
1
test_eval_scope.pl
#!/usr/bin/perl
use File::Basename;
use lib dirname (__FILE__);
use test_eval_scope;
test_eval_scope::f1();
When I run the program, I got the following error
$ test_eval_scope.pl
Variable "%h" is not available at (eval 1) line 1.
My question is why the variable %h is out of scope.
I have done some modification, and found the following:
If I run without eval(), as in the above comment, it will work.
meaning that %h should be in the scope.
If I just add a seemingly useless mentioning in the code, as in the above
comment, eval() will work too.
If I combine pl and pm file into one file, eval() will work too.
If I declare %h with 'our' instead of 'my', eval() will work too.
I encountered this question when I was writing a big program which parsed user-provided code during run time. I don't need solutions as I have plenty workarounds above. But I cannot explain why the above code doesn't work. This affects my perl pride.
My perl version is v5.26.1 on linux.
Thank you for your help!
Subs only capture variables they use. Since f1 doesn't use %h, it doesn't capture it, and %h becomes inaccessible to f1 after it goes out of scope when the module finishes executing.
Any reference to the var, including one that's optimized away, causes the sub to capture the variable. As such, the following does work:
sub f1 {
%h if 0;
eval 'print %h, "\n"';
}
Demo:
$ perl -M5.010 -we'
{
my $x = "x";
sub f { eval q{$x} }
sub g { $x if 0; eval q{$x} }
}
say "f: ", f();
say "g: ", g();
'
Variable "$x" is not available at (eval 1) line 1.
Use of uninitialized value in say at -e line 8.
f:
g: x
How can I escape a semi-colon (that's syntax ) in perl by only adding characters after it?
Say I have a line of code:
print "foo";
I want to add the following code after it so it can repeat 5 times:
print "foo"; x 5;
Is there anyway I can escape/ignore the semicolon (without altering the original piece of code) so it can be interpreted as:
print "foo" x 5;
Edit: This seems like too much of a hassle, better off to just nest the line in a for loop.
Seems like you just want to print "foo" 5 times ?
If yes, then :
use strict;
use warnings;
print "foo" for (1..5);
Yields :
foofoofoofoofoo
Here is an example using a source filter.
In fact it alters the code before executing it so you don't have to do it yourself.
echo package FiveTimes; use Filter::Simple sub{s/;/x5;/g};1; > FiveTimes.pm
perl -MFiveTimes -e"print qq/foo/;"
foofoofoofoofoo
print "foo"; BEGIN{tie *STDOUT,'FiveTimer';sub FiveTimer::TIEHANDLE{bless{},'FiveTimer'}sub FiveTimer::PRINT{CORE::print STDERR $_[1] x 5}}
or more readably
print "foo"; BEGIN{ tie *STDOUT,'FiveTimer' }
sub FiveTimer::TIEHANDLE{bless{},'FiveTimer'}
sub FiveTimer::PRINT{CORE::print STDERR $_[1] x 5}}
I'm trying to scan a file for lines containing a specific string, and print the lines to another file.
However, I need to print out multiple lines until ")" character IF the line containing the string ended in "," ignoring whitespaces.
Currently I'm using
for func in $fnnames
do
sed/"$func"/p <$file >>$CODEBASEDIR/function_signature -n
done
where $func contains the string I look for, but of course it doesn't work for the restriction.
Is there a way to do this? Currently using bash, but perl is fine also.
Thanks.
Your question is tricky because your restrictions are not precise. You say - I think - that a block should look like this:
foo,
bar,
baz)
Where foo is the string that starts the block, and closing parenthesis ends it. However, you could also be saying:
foo bar baz) xxxxxxxxxxx,
And you only want to print until the ), which is to say foo bar baz), IF the line ends with comma.
You could also be saying that only lines that end with a comma should be continued:
foo, # print + is continued
bar # print + is not continued
xxxxx # ignored line
foo # print + is not continued
foo,
bar,
baz) # closing parens also end block
Since I can only guess that you mean the first alternative, I give you two options:
use strict;
use warnings;
sub flip {
while (<DATA>) {
print if /^foo/ .. /\)\s*$/;
}
}
sub ifchain {
my ($foo, $print);
while (<DATA>) {
if (/^foo/) {
$foo = 1; # start block
print;
} elsif ($foo) {
if (/,\s*$/) {
print;
} elsif (/\)\s*$/) {
$foo = 0; # end block
print;
}
# for catching input errors:
else { chomp; warn "Mismatched line '$_'" }
}
}
}
__DATA__
foo1,
bar,
baz)
sadsdasdasdasd,
asda
adaffssd
foo2,
two,
three)
yada
The first one will print any lines found between a line starting with foo and a line ending with ). It will ignore the "lines end with comma" restriction. On the positive side, it can be simplified to a one-liner:
perl -ne 'print if /^foo/ .. /\)\s*$/' file.txt
The second one is just a simplistic if-structure that will consider both restrictions, and warn (print to STDERR) if it finds a line inside a block that does not match both.
perl -ne 'print if 1 .. /\).*,\s*$/'
In Perl most of my print statements take the form
print "hello." . "\n";
Is there a nice way to avoid keeping all the pesky "\n"s lying around?
I know I could make a new function such as myprint that automatically appends \n, but it would be nice if I could override the existing print.
Raku (Perl 6) has the say function that automatically appends \n.
You can also use say in Perl 5.10 or 5.12 if you add
use feature qw(say);
to the beginning of your program. Or you can use Modern::Perl to get this and other features.
See perldoc feature for more details.
You can use the -l option in the she-bang header:
#!/usr/bin/perl -l
$text = "hello";
print $text;
print $text;
Output:
hello
hello
See "-l[octnum]" in perlrun(1) for details.
If Perl 5.10+ is not an option, here is a quick and dirty approximation. It's not exactly the same, since say has some magic when its first arg is a handle, but for printing to STDOUT:
sub say {print #_, "\n"}
say 'hello';
The way you're writing your print statement is unnecessarily verbose. There's no need to separate the newline into its own string. This is sufficient.
print "hello.\n";
This realization will probably make your coding easier in general.
In addition to using use feature "say" or use 5.10.0 or use Modern::Perl to get the built in say feature, I'm going to pimp perl5i which turns on a lot of sensible missing Perl 5 features by default.
Perhaps you want to change your output record separator to linefeed with:
local $\ = "\n";
$ perl -e 'print q{hello};print q{goodbye}' | od -c
0000000 h e l l o g o o d b y e
0000014
$ perl -e '$\ = qq{\n}; print q{hello};print q{goodbye}' | od -c
0000000 h e l l o \n g o o d b y e \n
0000016
Update: my answer speaks to capability rather than advisability. I don't regard adding "\n" at the end of lines to be a "pesky" chore, but if someone really wants to avoid them, this is one way. If I had to maintain a bit of code that uses this technique, I'd probably refactor it out pronto.
Here's what I found at https://perldoc.perl.org/perlvar.html:
$\
The output record separator for the print operator. If defined, this value is
printed after the last of print's arguments. Default is undef.
You cannot call output_record_separator() on a handle, only as a static method.
See IO::Handle.
Mnemonic: you set $\ instead of adding "\n" at the end of the print. Also, it's
just like $/ , but it's what you get "back" from Perl.
example:
$\ = "\n";
print "a newline will be appended to the end of this line automatically";
In Raku (Perl 6) there is, the say function.
If you're stuck with pre-5.10, then the solutions provided above will not fully replicate the say function. For example
sub say { print #_, "\n"; }
Will not work with invocations such as
say for #arr;
or
for (#arr) {
say;
}
... because the above function does not act on the implicit global $_ like print and the real say function.
To more closely replicate the perl 5.10+ say you want this function
sub say {
if (#_) { print #_, "\n"; }
else { print $_, "\n"; }
}
Which now acts like this
my #arr = qw( alpha beta gamma );
say #arr;
# OUTPUT
# alphabetagamma
#
say for #arr;
# OUTPUT
# alpha
# beta
# gamma
#
The say builtin in perl6 behaves a little differently. Invoking it with say #arr or #arr.say will not just concatenate the array items, but instead prints them separated with the list separator. To replicate this in perl5 you would do this
sub say {
if (#_) { print join($", #_) . "\n"; }
else { print $_ . "\n"; }
}
$" is the global list separator variable, or if you're using English.pm then is is $LIST_SEPARATOR
It will now act more like perl6, like so
say #arr;
# OUTPUT
# alpha beta gamma
#
As requested:
sub myprint { print #_, "\n"; }
myprint "foo", 3 . 'bar'
And what was the actual problem?
Maybe try understanding the language you have before trying to change it.
Man perlvar(1) says: "Within a subroutine the array #_ contains the parameters passed to that subroutine."
Suppose I have a situation where I'm trying to experiment with some Perl code.
perl -d foo.pl
Foo.pl chugs it's merry way around (it's a big script), and I decide I want to rerun a particular subroutine and single step through it, but without restarting the process. How would I do that?
The debugger command b method sets a breakpoint at the beginning of your subroutine.
DB<1> b foo
DB<2> &foo(12)
main::foo(foo.pl:2): my ($x) = #_;
DB<<3>> s
main::foo(foo.pl:3): $x += 3;
DB<<3>> s
main::foo(foo.pl:4): print "x = $x\n";
DB<<3>> _
Sometimes you may have to qualify the subroutine names with a package name.
DB<1> use MyModule
DB<2> b MyModule::MySubroutine
just do: func_name(args)
e.g.
sub foo {
my $arg = shift;
print "hello $arg\n";
}
In perl -d:
DB<1> foo('tom')
hello tom
Responding to the edit regarding wanting to re-step through a subroutine.
This is not entirely the most elegant way of doing this, but I don't have another method off the top of my head and am interested in other people's answers to this question :
my $stop_foo = 0;
while(not $stop_foo) {
foo();
}
sub foo {
my $a = 1 + 1;
}
The debugger will continually execute foo, but you can stop the next loop by executing '$stop_foo++' in the debugger.
Again, I don't really feel like that's the best way but it does get the job done with only minor additions to the debugged code.