I am trying to use grep to find all the callers of a particular C function.
E.g.:
void foo()
{
...
ret = my_bar()
}
For all occurrances of my_bar() I want to print the corresponding function name from where my_bar() is called.
I have tried (based on Regex (grep) for multi-line search needed)
grep -Pzo "(?s)^{\N*?.*?my_bar" *.c
using Perl regex, but this doesn't quite work as expected. It starts the match at the function before foo() till my_bar()
Is this possible with grep/perl and regex, or will I have to use tools like cscope?
A Perl one-liner that remembers the last function, and prints its name when my_bar() is found. This is quite simplistic, it'll print a function multiple times if it calls my_bar() multiple times, but you get the basic idea.
perl -ne '$fun = $_ if /^\w+ \w+\(.+\)$/; if (/my_bar\(\)/) { print "$fun" }' *.c
The variant below should cover functions with multi-line argument lists and trailing comments as well:
perl -ne '$fun = $_ if /^\w+ \w+\(.+$/; if (/my_bar\(\)/) { print "$fun" }' *.c
Related
I am new to perl programming and I am trying to build a script using several subroutines on it. For a start I am trying to run a short mocke script to work out subroutines behaviour, but I don't get to understand the input.
Here is my code:
sub prueba{
my (#array1, #array2)=#_;
if (scalar(#array1)<scalar(#array2)) {
print #array1,"\n";
} elsif (scalar(#array1)>scalar(#array2)){
print #array2,"\n";
}
};
my #primero=(1,5,9);
my #segundo=(1,7,8,9,6,5,6,9);
prueba(#primero,#segundo);
I am passing two arrays and I want the subroutine to retrieve the answer according to those arrays, but when I run it I get no output, not even warning errors messages... I already tried using the refference to the array, but still not working:
sub prueba{
my (#array1, #array2)=#_;
if (scalar(#array1)<scalar(#array2)) {
print #array1,"\n";
} elsif (scalar(#array1)>scalar(#array2)){
print #array2,"\n";
}
};
my #primero=(1,5,9);
my #segundo=(1,7,8,9,6,5,6,9);
prueba(\#primero,\#segundo);
You can't pass arrays to subs (and they can't return them). You can only pass a number of scalars. What you are doing is equivalent to the following:
prueba(1,5,9,1,7,8,9,6,5,6,9);
All of the arguments end up in #array1. What we do is pass references to arrays.
prueba(\#primero,\#segundo);
But that also requires changing the sub. Without change, all of the arguments still end up in #array1. See perlreftut for a start on working with references. You can use
sub prueba{
my ($array1, $array2)=#_;
if (scalar(#$array1)<scalar(#$array2)) {
print "#$array1\n";
} elsif (scalar(#$array1)>scalar(#$array2)){
print "#$array2\n";
}
}
or just
sub prueba {
my ($array1, $array2) = #_;
if (#$array1 < #$array2) { say "#$array1"; }
elsif (#$array1 > #$array2) { say "#$array2"; }
}
< and > expect a number, so they already impose scalar context. And might as well use say, though that requires use feature qw( say ); (or something like use 5.014; which does the trick as well).
You can use prototypes to make it look like you're passing multiple arrays, and have perl turn them automatically into references:
sub prueba :prototype(\#\#) {
my ($array1, $array2) = #_;
if (#$array1 < #$array2) {
print #$array1,"\n";
} elsif (#$array1 > #$array2){
print #$array2,"\n";
}
}
my #primero=(1,5,9);
my #segundo=(1,7,8,9,6,5,6,9);
prueba(#primero, #segundo);
But read the documentation carefully to understand the cases where the subroutine can be called without enforcing the prototype.
Thanks all I just figured out what I wanted. I found that I can actually pass an array to perl, however maybe I am not explaning mysfel properly.The thing is to load the arrays as follow, inside the subroutine.
my #primero=#{$_[0]};
my #segundo=#{$_[1]};
This means we are using the reference. Ehen running the function, we must write the \ before each input:
prueba(\#primero,\#segundo);
Let's say I have a file with the lines such as:
*some numbers* :00: *somenumbers*
*somenumbers* :21: *somenumbers*
And for every number between :: I need to count how many times it repeats in the file?
while (<>){
chomp($_);
my ($nebitno,$bitno,$opetnebitno) = split /:/, $_;
$count{$bitno}++;
}
foreach $bitno(sort keys %count){
print $bitno," ",$count{bitno}, "\n";
}
What you produced was not bad code — it did the job for a single file at a time. Adapting the code shown in the question to handle multiple files, resetting the counts after each file:
#!/usr/bin/perl
use strict;
use warnings;
my %count = ();
while (<>) {
my ($nebitno, $bitno, $opetnebitno) = split /:/, $_;
$count{$bitno}++;
}
continue
{
if (eof) {
print "$ARGV:\n";
foreach $bitno (sort keys %count) {
print "$bitno $count{bitno}\n";
}
%count = ();
}
}
The key here is the continue block, and the if (eof) test. You can use close $ARGV in a continue block to reset $. (the line number) when the file changes; it is a common use for it. This sort of per-file summary is another use. The other changes are cosmetic. You don't need to chomp the line (though there's no particular harm done if you do); I print whole strings rather than using comma-separated lists (it works well here and very often). I use a few more spaces. I left it with the 1TBS format for the blocks of code, though I don't use that myself (I use Allman).
My draft solution used practically the same printing code as shown above, but the main while loop was slightly different:
#!/usr/bin/env perl
use strict;
use warnings;
my %counts = ();
while (<>)
{
$counts{$1}++ if (m/.*:(\d+):/);
}
continue
{
if (eof)
{
print "$ARGV:\n";
foreach my $number (sort { $a <=> $b } keys %counts)
{
print ":$number: $counts{$number}\n"
}
%counts = ();
}
}
The only advantage over what you used is that if some line doesn't contain a colon-surrounded number, it ignores the line, whereas yours doesn't consider that possibility. I'm not sure the comparison code in the sort is necessary — it ensures that the comparisons are numeric, though. If the numbers are all the same length and zero-padded on the left when necessary, there's no problem. If they're more generally formatted, the 'forced numeric' comparison might make a difference.
Remember: this is Perl, so TMTOWDTI (There's More Than One Way To Do It). Someone else might come up with a simpler solution.
Desired output can be achieved with following code snippet
look for pattern :\d+: in a line
increment hash %count for the digit
output result to console
use strict;
use warnings;
use feature 'say';
my %count;
/:(\d+):/ && $count{$1}++ for <>;
say "$_ = $count{$_}" for sort keys %count;
I would like to have an array that contains a set of functions that I can iterate through and call. The issue is that the functions all run via the line that adds them to the array (ie. $scripts).
Example:
function Hello
{
$BadFunction = "Hello"
Write-Host "Hello!"
}
function HowAreYou
{
$BadFunction = "HowAreYou"
Write-Host "How are you?"
#$false = $true
}
function Goodbye
{
$BadFunction = "Goodbye"
Write-Host "Goodbye!"
}
$scripts = #((Hello), (HowAreYou), (Goodbye))
foreach ($script in $scripts)
{
$script
}
Functions can only be called by using their name, not referenced, but you can get the scriptblock via the Function: drive:
$scripts = $Function:Hello, $Function:HowAreYou, $Function:GoodBye
# call them with the & operator
$scripts | ForEach-Object { & $_ }
# You can also call them by calling Invoke on the scriptblock
$scripts | ForEach-Object Invoke
Joey's answer contains good information, but if all you need is to reference your functions by name, define your array as containing the function names as strings, and invoke the functions by those strings with &, the call operator:
function Hello { "Hello!" }
function HowAreYou { "How are you?" }
function Goodbye { "Goodbye!" }
# The array of function names *as strings*.
# Note that you don't need #(...) to create an array.
$funcs = 'Hello', 'HowAreYou', 'Goodbye'
foreach ($func in $funcs)
{
# Use & to call the function by the name stored in a variable.
& $func
}
The issue is that the functions all run via the line that adds them to the array (ie. $scripts).
That's because your array elements are expressions (due to the enclosing (...)) that call the functions (e.g., (Hello) calls Hello and makes its output the array element), given that they're being referenced without single- or double-quoting.
Unlike in, say, bash, you cannot define string array elements as barewords (tokens without enclosing single- or double-quoting); for instance, the following is a syntax error in PowerShell:
# !! Does NOT work - strings must be quoted.
$funcs = Hello, HowAreYou, GoodBye # ditto with #(...)
I have details like below in an array. There will be plenty of testbed details in actual case. I want to grep a particular testbed(TESTBED = vApp_eprapot_icr) and an infomation like below should get copied to another array. How can I do it using perl ? End of Testbed info can be understood by a closing flower bracket }.
TESTBED = vApp_eprapot_icr {
DEVICE = vApp_eprapot_icr-ipos1
DEVICE = vApp_eprapot_icr-ipos2
DEVICE = vApp_eprapot_icr-ipos3
DEVICE = vApp_eprapot_icr-ipos5
CARDS=1GIGE,ETHFAST
CARDS=3GIGE,ETHFAST
CARDS=10PGIGE,ETHFAST
CARDS=20PGIGE,ETHFAST
CARDS=40PGIGE,ETHFAST
CARDS=ETHFAST,ETHFAST
CARDS=10GIGE,ETHFAST
CARDS=ETH,ETHFAST
CARDS=10P10GIGE,ETHFAST
CARDS=PPA2GIGE,ETHFAST
CARDS=ETH,ETHFAST,ETHGIGE
}
I will make it simpler, please see the below array
#array("
student=Amit {
Age=20
sex=male
rollno=201
}
student=Akshaya {
Age=24
phone:88665544
sex=female
rollno=407
}
student=Akash {
Age=23
sex=male
rollno=356
address=na
phone=88456789
}
");
Consider an array like this. Where such entries are plenty. I need to grep, for an example student=Akshaya's data. from the opening '{' to closing '}' all info should get copied to another array. This is what I'm looking for.
while (<>) {
print if /TESTBED = vApp_eprapot_icr/../\}/;
}
as a sidenote <> will capture the filename you use on cmdline. So if the data is stored in a file you will run from commandline
perl scriptname.pl filename.txt
Ok. We finally have enough information to come up with an answer. Or, at least, to produce two answers which will work on slightly different versions of your input file.
In a comment you say that you are creating your array like this:
#array = `cat $file`;
That's not a very good idea for a couple of reasons. Firstly, why run an external command like cat when Perl will read the file for you. And secondly, this gives you one element in your array for each line in your input file. Things become far easier if you arrange it so that each of your TESTBED = foo { ... } records is a single array element.
Let's get rid of the cat first. The easiest way to read a single file into an array is to use the file input operator - <>. That will read data from the file whose name is given on the command line. So if you call your program filter_records, you can call it like this:
$ ./filter_records your_input_data.txt
And then read it into an array like this:
#array = <>;
That's good, but we still have each line of the input file in its own array element. How we fix that depends on the exact format of your input file. It's easiest if there's a blank line between each record in the input file, so it looks like this:
student=Amit {
Age=20
sex=male
rollno=201
}
student=Akshaya {
Age=24
phone:88665544
sex=female
rollno=407
}
student=Akash {
Age=23
sex=male
rollno=356
address=na
phone=88456789
}
Perl has a special variable called $/ which controls how it reads records from input files. If we set it to be an empty string then Perl goes into "paragraph" mode and it uses blank lines to delimit records. So we can write code like this:
{
local $/ = '';
#array = <>;
}
Note that it's always a good idea to localise changes to Perl's special variables, which is why I have enclosed the whole thing in a naked block.
If there are no blank lines, then things get slightly harder. We'll read the whole file in and then split it.
Here's our example file with no blank lines:
student=Amit {
Age=20
sex=male
rollno=201
}
student=Akshaya {
Age=24
phone:88665544
sex=female
rollno=407
}
student=Akash {
Age=23
sex=male
rollno=356
address=na
phone=88456789
}
And here's the code we use to read that data into an array.
{
local $/;
$data = <>;
}
#array = split /(?<=^})\n/m, $data;
This time, we've set $/ to undef which means that all of the data has been read from the file. We then split the data wherever we find a newline that is preceded by a } on a line by itself.
Whichever of the two solutions above that we use, we end up with an array which (for our sample data) has three elements - one for each of the records in our data file. It's then simple to use Perl's grep to filter that array in various ways:
# All students whose names start with 'Ak'
#filtered_array = grep { /student=Ak/ } #array;
If you use similar techniques on your original data file, then you can get the records that you are interested in with code like this:
#filtered_array = grep { /TESTBED = vApp_eprapot_icr/ } #array;
im trying the following:
I want to fork multiple processes and use multiple pipes (child -> parent) simultaneously.
My approach is to use IO::Pipe.
#!/usr/bin/perl
use strict;
use IO::Pipe;
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
my #ua_processes = (0..9);
my $url = "http://<some-sample-textfile>";
my #ua_pipe;
my #ua_process;
$ua_pipe[0] = IO::Pipe->new();
$ua_process[0] = fork();
if( $ua_process[0] == 0 ) {
my $response = $ua->get($url);
$ua_pipe[0]->writer();
print $ua_pipe[0] $response->decoded_content;
exit 0;
}
$ua_pipe[0]->reader();
while (<$ua_pipe[0]>) {
print $_;
}
In future i want to use multiple "$ua_process"s in an array.
After execution i got the following errors:
Scalar found where operator expected at ./forked.pl line 18, near "] $response"
(Missing operator before $response?)
syntax error at ./forked.pl line 18, near "] $response"
BEGIN not safe after errors--compilation aborted at ./forked.pl line 23.
If i dont use arrays, the same code works perfectly. It seems only the $ua_pipe[0] dont work as expected (together with a array).
I really dont know why. Anyone knows a solution? Help would be very appreciated!
Your problem is here:
print $ua_pipe[0] $response->decoded_content;
The print and say builtins use the indirect syntax to specify the file handle. This allows only for a single scalar variable or a bareword:
print STDOUT "foo";
or
print $file "foo";
If you want to specify the file handle via a more complex expression, you have to enclose that expression in curlies; this is called a dative block:
print { $ua_pipe[0] } $response-decoded_content;
This should now work fine.
Edit
I overlooked the <$ua_pipe[0]>. The readline operator <> also doubles as the glob operator (i.e. does shell expansion for patterns like *.txt). Here, the same rules as for say and print apply: It'll only use the file handle if it is a bareword or a simple scalar variable. Otherwise, it will be interpreted as a glob pattern (implying stringification of the argument). To disambiguate:
For the readline <>, we have to resort to the readline builtin:
while (readline $ua_pipe[0]) { ... }
To force globbing <>, pass it a string: <"some*.pattern">, or preferably use the glob builtin.