Perl file exists test fails when path contains spaces - perl

Below is an example of what I am talking about
$PathPlusFileName gets set in Gtk2 File Chooser dialog SUB
if (-e $PathPlusFileName) {
print "found file\n";
} else {
print "did not find file\n";
}
IF block works as long as there are no spaces in the PATH name.
Did try double quoting the path, didn't help.
Any suggestions on how to correct would be appreciated.

It works fine:
#!/usr/bin/perl
use strict;
use warnings;
my $filename = 'name with spaces';
open ( my $testfile, '>', $filename ) or warn $!;
if ( -e "name with spaces" ) {
print "\"$filename\" exists\n";
}
I suspect you'll find something else is going wrong. My first guess would be - did you remember to chomp your input? But we can't answer without seeing how this value is set.

As it turn out, the previous programmer that work on the package GUI section had left this line of test code active.
$PathPlusFileName =~ s/ /\ /g;
One it was removed, if(-e worked as expected. Thank all for the help and suggestions!

Just put your variable in quotes:
if (-e "$PathPlusFileName")

Related

How to check for a string using Perl given

I'm trying to replace a particular line in a file. I can get my program to run, but it doesn't actually do the replacing that I want it to.
Here is my sample file:
test line 1
test line 2
line to be overwritten
test line 3
Here is the code that I have:
my $origFile = $file_path . "junk\.file";
my $newFile = $file_path . "junk\.file\.backup";
# system command to make a backup of the file
system "mv $origFile $newFile";
#opens the files
open( my $INFILE, $newFile ) || die "Unable to read $newFile\n";
open( my $OUTFILE, '>' . $origFile ) || die "Unable to create $origFile\n";
# While loop to read in the file line by line
while ( <$INFILE> ) {
given ($_) {
when ("line to be overwritten") {
print $OUTFILE "line has been overwritten\n";
}
default {
print $OUTFILE $_;
}
}
}
close($INFILE);
close($OUTFILE);
I've tried to change the when statements several different ways to no avail:
when ($_ eq "line to be overwritten")
when ($_ == "line to be overwritten")
when ($_ cmp "line to be overwritten")
But those only generate errors. Anyone know what I'm doing wrong here?
As highlighted in a comment on the original question, given/when is an experimental feature of perl. I would personally recommend using if/else in a loop, and then either use string equality or a regex to match the line(s) you want to replace. A quick example:
use strict;
use warnings;
while(my $line = <DATA>) {
if ( $line =~ /line to be overwritten/ ) {
print "Overwritten\n";
} else {
print $line;
}
}
__DATA__
test line 1
test line 2
line to be overwritten
test line 3
This will give the output:
test line 1
test line 2
Overwritten
test line 3
You could also use the string equality if you aren't confident in your regex, or the string is guaranteed to be the same:
...
if ($line eq 'line to be overwritten') {
...
Sidenotes
open
On your initial open, it is recommended to use the 3 argument version of open to save from unexpected issues:
open(my $INFILE, '<', $newFile) || die "Unable to read $newFile\n";
(for more info on this, see here: http://modernperlbooks.com/mt/2010/04/three-arg-open-migrating-to-modern-perl.html)
strict & warnings
Also, it is recommended to use strict and warnings in your code file, as seen in my example above - this will save you from accidental mistakes like trying to use a variable which has not been declared, and syntax errors which may give you head-scratching results!
Experimental Features
Experimental features in perl are where there is no guarantee made for backwards compatibility to be maintained when a new release of perl comes out. Obviously if you are using the same version of perl everywhere it should be compatible, but things may break if you update to another major version of perl. answered here as I dont have the reputation to answer in the comments...
You seem to be making it way more complicated than it needs to be - a simple regex to check each line and act accordingly should do the job.
while(<$INFILE>)
{
chomp($_);
if /^line to be overwritten$/ )
{
print $OUTFILE "line has been overwritten\n";
}
else
{
print $OUTFILE "$_\n";
}
}
One way to do it is to use Tie::File module. It allows to replace data right in the file. You can make the backup same way you are currently doing, before changing the original file.
use strict;
use warnings;
use Tie::File;
my $file = 'test.txt';
tie my #textFile, 'Tie::File', $file, recsep => "\n" or die $!;
s/line to be overwritten/line has been overwritten/ for #textFile;
untie #textFile;

Reading file line by line iteration issue

I have the following simple piece of code (identified as the problem piece of code and extracted from a much larger program).
Is it me or can you see an obvious error in this code that it stopping it from matching against $variable and printing $found when it definitely should be doing?
Nothing is printed when I try to print $variable, and there are definitely matching lines in the file I am using.
The code:
if (defined $var) {
open (MESSAGES, "<$messages") or die $!;
my $theText = $mech->content( format => 'text' );
print "$theText\n";
foreach my $variable (<MESSAGES>) {
chomp ($variable);
print "$variable\n";
if ($theText =~ m/$variable/) {
print "FOUND\n";
}
}
}
I have located this as the point at which the error is occurring but cannot understand why?
There may be something I am totally overlooking as its very late?
Update I have since realised that I misread your question and this probably doesn't solve the problem. However the points are valid so I am leaving them here.
You probably have regular expression metacharacters in $variable. The line
if ($theText =~ m/$variable/) { ... }
should be
if ($theText =~ m/\Q$variable/) { ... }
to escape any that there are.
But are you sure you don't just want eq?
In addition, you should read from the file using
while (my $variable = <MESSAGES>) { ... }
as a for loop will unnecessarily read the entire file into memory. And please use a better name than $variable.
This works for me.. Am I missing the question at hand? You're just trying to match "$theText" to anything on each line in the file right?
#!/usr/bin/perl
use warnings;
use strict;
my $fh;
my $filename = $ARGV[0] or die "$0 filename\n";
open $fh, "<", $filename;
my $match_text = "whatever";
my $matched = '';
# I would use a while loop, out of habit here
#while(my $line = <$fh>) {
foreach my $line (<$fh>) {
$matched =
$line =~ m/$match_text/ ? "Matched" : "Not matched";
print $matched . ": " . $line;
}
close $fh
./test.pl testfile
Not matched: this is some textfile
Matched: with a bunch of lines or whatever and
Not matched: whatnot....
Edit: Ah, I see.. Why don't you try printing before and after the "chomp()" and see what you get? That shouldn't be the issue, but it doesn't hurt to test each case..

How to get filename of a opened file?

I have this simple script that I'm working on. I must admit, I'm totally new to PERL and kinda stuck with this stupid problem.
open(IN, "<def/t.html") or die();
while(<IN>) {
chomp;
if($_ =~ m/FF0000/) {
print "\n".$_."\n";
}
}
So... I opened the t.html and found the given string in the file. Output was ok, but I need also filename of a file in which string was found, to be printed. I really don't know how to return this, and I need it right after the $_. Thanks for the help in advance.
Simply save the file name in a variable before you open it, then go from there:
my $filename = 'def/t.html';
open( IN, '<', $filename ) or die $!;
...
print "\n$filename: " . $_ . "\n";
Notice that the above uses the 3-arg form of open(), which is safer.
(Also, the language is "Perl", not "PERL".)
That is a strange idea, but you can if you want:
$ cat 1.pl
#somewhere in the code
open(F, "f.txt");
my $f = fileno(F);
#here you want to find the filename
open(FILENAME, "ls -l /proc/$$/fd/$f|");
my #fn = split(/\s+/, <FILENAME>);
print $fn[$#fn],"\n";
$ perl 1.pl
/home/ic/f.txt
Here you know only the filedescriptor and find the filename using it.
You can also write it much shorter with readlink:
open(F, "f.txt");
my $f = fileno(F);
#here you want to find the filename
print readlink("/proc/$$/fd/$f"), "\n";
I must note that the file can be already deleted (but it exists still if it is open).

How can I search multiple files for a string in Perl?

My question is probably simple but I'm a complete newbie. I want to search the contents of multiple text files for a particular phrase and then display the lines of the finds on screen. I've already learnt how to deal with a single file. For example, if I want to search for a word, say "Okay" in a text file named "wyvern.txt" in the root directory of F. The following code works:
#!/usr/bin/perl
$file = 'F:\wyvern.txt';
open(txt, $file);
while($line = <txt>) {
print "$line" if $line =~ /Okay/;
}
close(txt);
But what should I do if I want to search for the same phrase in two text files, say "wyvern' and "casanova" respectively? or how about all the files in the directory "novels" in the root directory of F.
Any help would be greatly appreciated.
Thanks in advance
Mike
Edit:
Haha, I finally figured out how to search all the files in a directory for a pattern match:)
The following code works great:
#!/usr/bin/perl
#files = <F:/novels/*>;
foreach $file (#files) {
open (FILE, "$file");
while($line= <FILE> ){
print "$line" if $line =~ /Okay/;
}
close FILE;
}
Extending the good answer provided by Jonathan Leffler:
The filename where the match was found is in $ARGV, and with a small change, the line number can be found in $.. Example:
while (<>) {
print "$ARGV:$.:$_" if /pattern/;
} continue {
close ARGV if eof; # Reset $. at the end of each file.
}
Furthermore, if you have a list of filenames and they're not on the commandline, you can still get the magic ARGV behavior. Watch:
{
local #ARGV = ('one.txt', 'two.txt');
while (<>) {
print "$ARGV:$.:$_" if /Okay/;
} continue {
close ARGV if eof;
}
}
Which is a generally useful pattern for doing line-by-line processing on a series of files, whatever it is -- even if I might recommend File::Grep or App::Ack for this specific problem :)
On a system where command line arguments are properly expanded, you can use:
[sinan#host:~/test]$ perl -ne 'print "$.:$_" if /config/' *
1:$(srcdir)/config/override.m4
The problem with Windows is:
C:\Temp> perl -ne "print if /perl/" *.txt
Can't open *.txt: Invalid argument.
On Windows, you could do:
C:\Temp> for %f in (*.txt) do perl -ne "print if /perl/" %f
But, you might just want to use cmd.exe builtin findstr or the grep command line tool.
The easiest way is to list the files on the command line, and then simply use:
while (<>)
{
print if m/Okay/;
}
File::Grep is what you need here
Just a tweak on your line: <F:/novels/*>, I prefer to use the glob keyword - it works the same in this context and avoids the chances of confusing the many different uses of angle brackets in perl. Ie:
#files = glob "F:/novels/*";
See perldoc glob for more.
put the files in a for loop, or something along those lines:
i.e.
for $file ('F:\wyvern.txt','F:\casanova.txt') {
open(TXT, $file);
while($line = <txt>) {
print "$line" if $line =~ /Okay/;
}
close TXT;
}
Okay, I'm a complete dummie. But to sum up, I now can search one single text file or multiple text files for a specified string. I'm still trying to figuring out how to deal with all the files in one folder.
the following codes work.
Code 1:
#!/usr/bin/perl
$file = 'F:\one.txt';
open(txt, $file);
while($line = <txt>) {
print "$line" if $line =~ /Okay/;
}
close(txt);
Code 2:
#!/usr/bin/perl
{
local #ARGV = ('F:\wyvern.txt', 'F:\casanova.txt');
while (<>) {
print "$ARGV:$.:$_" if /Okay/;
} continue {
close ARGV if eof;
}
}
Thanks again for your help. I really appreciate it.

Perl: Looping over input lines with an index-based approach

This is a beginner-best-practice question in perl. I'm new to this language. The question is:
If I want to process the output lines from a program, how can I format THE FIRST LINE in a special way?
I think of two possibilities:
1) A flag variable, once the loop is executed first time is set. But it will be evaluated for each cycle. BAD solution
2) An index-based loop (like a "for"). Then I would start the loop in i=1. This solution is far better. The problem is HOW CAN I DO IT?
I just found the code for looping over with the while ( <> ) construct.
Here you can see better:
$command_string = "par-format 70j p0 s0 < " . $ARGV[0] . "|\n";
open DATA, $command_string or die "Couldn't execute program: $!";
print "\t <div>&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;|-- <strong>Description</strong></div>\n";
while ( defined( my $line = <DATA> ) ) {
chomp($line);
# print "$line\n";
print "\t <div>&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;&‎nbsp;|&‎nbsp;&‎nbsp;&‎nbsp;-- " . $line . "</div>\n";
}
close DATA;
Please also don't hesitate in correcting any code in here, this is my first perl poem.
Thanks!
You can always use $. or the English name $INPUT_LINE_NUMBER to control the logic in your loop with:
while (my $line = <>) {
if ($. == 1) {
# do cool stuff here
}
# do normal stuff here
}
To handle the first line differently, you could just put
$line = <DATA>;
above your loop.
With proper checking for read problems (empty file, etc.) this should be
if ($line = <DATA>) {
...do special things...
}
while (my $line = <DATA>) {
...do regular things...
}
I'm not sure about the defined() call. You might not need it, since an empty string has a false truth value.
From a 'best practices' perspective there is much wrong with that code sample:
open DATA, $command_string or die "Couldn't execute program: $!";
Security hole, please exploit me.
DATA is a magical value that points to a __DATA__ section at the end of the current file.
You should use
open my $fh
Which uses a lexical variable for a file handle instead of a global.
You should use 3 arg open, ie:
open my $fh, '<' , $filename
open my $fh, '-|' , $command
open my $fh, '-|' , $command, #args
sadly I have yet to work out how 3-arg works with dual-pipes.
theres' this IPC::Open2 thing, but I haven't worked out how
to use that effectively yet. Suggestions welcome .