Can grep be used on a Perl variable? - perl

Is it possible one way or another to, within a Perl script, effectively execute grep against a Perl variable? An equivalent Perl function would be equally acceptable, I just want to keep the solution as simple as possible.
For example:
#!/usr/bin/perl
#!/bin/grep
$var="foobar";
$newvar="system('grep -o "foo" $var');
sprintf $newvar;
Where I expect sprintf $newvar to output foo.
Would also welcome any feedback on best practice here. I am not extremely familiar with Perl.

you can just use regex matching in Perl. No need to call external "grep" command.
$var =~ /foo/;
please read documentation perlrequick for introduction on how to search for patterns in variables. Also of interest is Perl's own grep.
$var="foobar";
if ( $var =~ /foo/){
print "found foo\n";
}

Related

How do you check for the existence of file names with a specific string in Perl

I am rewriting a Bash script in Perl in order to learn the latter.
The script creates a file using the current date in a custom format and a ".txt" extension but checks first to make sure no file with the date in question already exists.
In Bash, I accomplish this with ls |grep $customDate as a condition. That is, if ls |grep $customDate is true, a warning is issued and no file is create while if ls |grep $customDate is false, the file gets created with the custom date plus a ".txt" extension
How can I mimic this in Perl?
For testing purposes, I wrote the code below but it does not print out anything - even when I have created a file that meets the condition:
use POSIX qw( strftime );
$customDate = strftime "%Y_%m%b_%d%a", localtime;
opendir(DIR, ".") or die "$!";
my #FILES = grep { /${customDate}*/ } readdir(DIR);
closedir(DIR);
print "$_\n" for #FILES;
I apologize if my question is unclear
"I am rewriting a Bash script in Perl in order to learn the latter."
I think you're taking the wrong approach to learning Perl, or to learning any language.
While there are always a lot of similarities between procedural languages, it is always wrong to focus on those above the differences. Programming languages must be learned from scratch if you hope ever to be able to read and write them well
I regularly see Perl code on Stack Overflow that has clearly been written by someone with the wrong head on. For instance, the clearest signs of a C programmer are
Declaring everything in one block at the top of a source file
Over-use of scalar and parentheses
Under-use of the default variable $_ and regular expressions
Using the C-style for loop, which usually looks something like this in Perl
my $i;
for ($i=0; $i<=scalar(#data); $i++)
{
process($data[$i])
}
Apart from ignoring perlstyle completely, the author is grasping for something familiar instead of embracing the new language. In idiomatic Perl that should look like
process($_) for #data
Reaching further, it is easy to become complacent about the consequences of phrases you may be writing glibly in the shell
You need to be aware that your shell statement
ls |grep $customDate
is starting new processes to run /bin/ls and /bin/grep, and piping information between them and back to the shell process. The Linux shell and its supporting utilities are designed to get trivial jobs done easily, but I believe they are being used too much with elaborate shell script one-liners that are opaque and beyond debugging
It's very hard to tell exactly what it is that you need your program to do, but it's looking like you want to discover whether there are any files that contain a string like 2016_05May_30Mon in the current directory
I have to say that's a horrible date-time format and I've struggling to believe that it's what you want, but I would prefer Perl's core Time::Piece module over POSIX any time
In this instance I would also make use od Perl's regular expressions, the -X *file test operators, and Perl's glob operator instead of opendir, readdir, closedir. None of those have a direct equivalent in any shell language
So, assuming that my guesses about your intention are correct, I would write this
use strict;
use warnings 'all';
use feature 'say';
use Time::Piece;
my $dtime = localtime()->strftime('%Y_%m%b_%d%a');
say for grep { -f and /$dtime/ } glob '*.txt';
which isn't remotely like your translation from shell to Perl
The reason you're not getting what you expect is the * in the grep is looking for the last character of the "$customDate" repeated as many times as it likes (which is not what you expect from the * in this case).
If your file has a "somedata.txt" ext, you should update the code as such, which will look for your date string then any number of characters followed by a txt:
$customDate = strftime "%Y_%m%b_%d%a", localtime;
opendir(DIR, ".") or die "$!";
my #FILES = grep { /${customDate}.*\.txt/ } readdir(DIR);
closedir(DIR);
print "$_\n" for #FILES;

experimenting with two interacting perl scripts

i am kinda new to perl and to programming in general. right now i am trying to learn a bit more about how i can make two perl scripts interact, and about parent/child processes.
for that purpose, i wrote two little perl scripts (a.pl and b.pl) to teach myself a little more about these things:
a.pl:
#!/usr/bin/env perl
use strict;
use warnings;
print "\npick a card, any card you want!\n>";
my $card = <STDIN>;
my #cmd = ('./b.pl');
push #cmd, $card;
system(#cmd);
print "sorry, i can't tell you the trick. magician's code...\n";
b.pl:
#!/usr/bin/env perl
use strict;
use warnings;
my $card = "#ARGV";
print "\nis this your card?\n\n$card\n(y/n)>";
chomp(my $answer = <STDIN>);
exit if $answer eq "y";
print "will i ever be a true magician?\n\n" if $answer eq "n";
there are a few things about this that i would like to ask for some advice. first of all: the reason i passed the reaction to "y" back to the first script is that i wanted to understand how parent/child processes work. if i understood the perldoc of "system" correctly, this function puts the parent process (in my case a.pl) on hold until the child process (b.pl) is finished. now that explains why a.pl is being completed when b.pl dies under the condition "if $answer eq "y" ". but what if i want the parent process to die and the child process to continue under a certain condition (for example "if $answer eq "n" ")? as it is right now, the program would print both statements (the one from a.pl and from b.pl) when the second STDIN is answere with "n". what would be a smart way to do that?
another thing i was wondering about is that when i wrote the scripts, i put lines 5-9 of a.pl like this:
my $card = <STDIN>;
system('./b.pl $card');
which didnt work, because no matter what i entered into STDIN, the system function did not pass any arguments and #ARGV in b.pl always returned 0. is this because you cannot use a variable as an argument of system, or is there something wrong with the syntax?
as i said before, i am trying to learn as much as i can about perl and how programming works, so if you guys have any tips or tweaks on how to make this better, i would be more than happy!
thanks for your help, i really appreciate it!
Prawn
Usually you don't want to use system() to spawn a second perl process...
There are many cases where different perl processes need to "talk" to each other...
For general information about IPC (Inter Process Communication) see here.
The (perhaps) most common way to exchange information between different perl processes is "sockets": IO::Socket.
The other thing,
system('./b.pl $card');
That is because you are using the single quote, if you used double quotes it should have worked. Single quotes are used as a literal string. In double quotes the variables are replaced with their value.
system("./b.pl $card");
More info on quotes:
http://www.perlmonks.org/?node=quotes+in+Perl
(Unfortunately I cannot completely answer your question)

What does -l $_ do in Perl, and how does it work?

What is the meaning of the nest code
foreach (#items)
{
if (-l $_) ## this is what I don't understand: the meaning of -l
{
...
}
}
Thanks for any help.
Let's look at each thing:
foreach (#items) {
...
}
This for loop (foreach and for are the same command in Perl) is taking each item from the #items list, and setting it to $_. The $_ is a special variable in Perl that is used as sort of a default variable. The idea is that you could do things like this:
foreach (#items) {
s/foo/bar/;
uc;
print;
}
And each of those command would operate on that $_ variable! If you simply say print with nothing else, it would print whatever is in $_. If you say uc and didn't mention a variable, it would uppercase whatever is in $_.
This is now discouraged for several reasons. First, $_ is global, so there might be side effects that are not intended. For example, imagine you call a subroutine that mucked with the value of $_. You would suddenly be surprised that your program doesn't work.
The other -l is a test operator. This operator checks whether the file given is a symbolic link or not. I've linked to the Perldoc that explains all of the test operators.
If you're not knowledgeable in Unix or BASH/Korn/Bourne shell scripting, having a command that starts with a dash just looks weird. However, much of Perl's syntax was stolen... I mean borrowed from Unix shell and awk commands. In Unix, there's a command called test which you can use like this:
if test -L $FILE
then
....
fi
In Unix, that -L is a parameter to the test command, and in Unix, most parameters to commands start with dashes. Perl simply borrowed the same syntax dash and all.
Interestingly, if you read the Perldoc for these test commands, you will notice that like the foreach loop, the various test commands will use the $_ variable if you don't give it a variable or file name. Whoever wrote that script could have written their loop like this:
foreach (#items)
{
if (-l) ## Notice no mention of the `$_` variable
{
...
}
}
Yeah, that's soooo much clear!
Just for your information, The modern way as recommended by many Perl experts (cough Damian Conway cough) is to avoid the $_ variable whenever possible since it doesn't really add clarity and can cause problems. He also recommends just saying for and forgetting foreach, and using curly braces on the same line:
for my $file (#items) {
if ( -l $file ) {
...
}
}
That might not help with the -l command, but at least you can see you're dealing with files, so you might suspect that -l has something to do with files.
Unfortunately, the Perldoc puts all of these file tests under the -X section and alphabetized under X, so if you're searching the Perldoc for a -l command, or any command that starts with a dash, you won't find it unless you know. However, at least you know now for the future where to look when you see something like this: -s $file.
It's an operator that checks if a file is a symbolic link.
The -l filetest operator checks whether a file is a symbolic link.
The way -l works under the hood resembles the code below.
#! /usr/bin/env perl
use strict;
use warnings;
use Fcntl ':mode';
sub is_symlink {
my($path) = #_;
my $mode = (lstat $path)[2];
die "$0: lstat $path: $!" unless defined $mode;
return S_ISLNK $mode;
}
my #items = #ARGV;
foreach (#items) {
if (is_symlink $_) {
print "$0: link: $_\n";
}
}
Sample output:
$ ln -s foo/bar/baz quux
$ ./flag-links flag-links quux
./flag-links: link: quux
Note the call to lstat and not stat because the latter would attempt to follow symlinks but never identify them!
To understand how Unix mode bits work, see the accepted answer to “understanding and decoding the file mode value from stat function output.”
From perldoc :
-l File is a symbolic link.

perl s/this/that/r ==> "Bareword found where operator expected"

Perl docs recommend this:
$foo = $bar =~ s/this/that/r;
However, I get this error:
Bareword found where operator expected near
"s/this/that/r" (#1)
This is specific to the r modifier, without it the code works.
However, I do not want to modify $bar.
I can, of course, replace
my $foo = $bar =~ s/this/that/r;
with
my $foo = $bar;
$foo =~ s/this/that/;
Is there a better solution?
As ruakh wrote, /r is new in perl 5.14. However you can do this in previous versions of perl:
(my $foo = $bar) =~ s/this/that/;
There's no better solution, no (though I usually write it on one line, since the s/// is essentially serving as part of the initialization process:
my $foo = $bar; $foo =~ s/this/that/;
By the way, the reason for your error-message is almost certainly that you're running a version of Perl that doesn't support the /r flag. That flag was added quite recently, in Perl 5.14. You might find it easier to develop using the documentation for your own version; for example, http://perldoc.perl.org/5.12.4/perlop.html if you're on Perl 5.12.4.
For completeness.
If you are stuck with an older version of perl.
And really want to use the s/// command without resorting to using a temporary variable.
Here is one way:
perl -E 'say map { s/_iter\d+\s*$//; $_ } $ENV{PWD}'
Basically use map to transform a copy of the string and return the final output.
Instead of what s/// does - of returning the count of substitutions.

What Perl module can I use to test CGI output for common errors?

Is there a Perl module which can test the CGI output of another program? E.g. I have a program
x.cgi
(this program is not in Perl) and I want to run it from program
test_x_cgi.pl
So, e.g. test_x_cgi.pl is something like
#!perl
use IPC::Run3
run3 (("x.cgi"), ...)
So in test_x_cgi.pl I want to automatically check that the output of x.cgi doesn't do stupid things like, e.g. print messages before the HTTP header is fully outputted. In other words, I want to have a kind of "browser" in Perl which processes the output. Before I try to create such a thing myself, is there any module on CPAN which does this?
Please note that x.cgi here is not a Perl script; I am trying to write a test framework for it in Perl. So, specifically, I want to test a string of output for ill-formedness.
Edit: Thanks
I have already written a module which does what I want, so feel free to answer this question for the benefit of other people, but any further answers are academic as far as I'm concerned.
There's CGI::Test, which looks like what you're looking for. It specifically mentions the ability to test non-Perl CGI programs. It hasn't been updated for a while, but neither has the CGI spec.
There is Test::HTTP. I have not used it, but seems to have an interface that fits your requirements.
$test->header_is($header_name, $value [, $description]);
Compares the response header
$header_name with the value $value
using Test::Builder-is>.
$test->header_like($header_name, $regex, [, $description]);
Compares the response header
$header_name with the regex $regex
using Test::Builder-like>.
Look at the examples from chapter 16 from the perl cookbook
16.9. Controlling the Input, Output, and Error of Another Program
It uses IPC::Open3.
Fom perl cookbook, might be modified by me, see below.
Example 16.2
cmd3sel - control all three of kids in, out, and error.
use IPC::Open3;
use IO::Select;
$cmd = "grep vt33 /none/such - /etc/termcap";
my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd);
$SIG{CHLD} = sub {
print "REAPER: status $? on $pid\n" if waitpid($pid, 0) > 0
};
#print CMD_IN "test test 1 2 3 \n";
close(CMD_IN);
my $selector = IO::Select->new();
$selector->add(*CMD_ERR, *CMD_OUT);
while (my #ready = $selector->can_read) {
foreach my $fh (#ready) {
if (fileno($fh) == fileno(CMD_ERR)) {print "STDERR: ", scalar <CMD_ERR>}
else {print "STDOUT: ", scalar <CMD_OUT>}
$selector->remove($fh) if eof($fh);
}
}
close(CMD_OUT);
close(CMD_ERR);
If you want to check that the output of x.cgi is properly formatted HTML/XHTML/XML/etc, why not run it through the W3 Validator?
You can download the source and find some way to call it from your Perl test script. Or, you might able to leverage this Perl interface to calling the W3 Validator on the web.
If you want to write a testing framework, I'd suggest taking a look at Test::More from CPAN as a good starting point. It's powerful but fairly easy to use and is definitely going to be better than cobbling something together as a one-off.