1

I would like to capture a difference between files in CWD and files in @files:

#!/usr/bin/perl -w
use Cwd qw[getcwd abs_path];
opendir CWD, getcwd;
@files=grep{!/^\./}readdir CWD;

push @files, ("foo.txt", "bar.txt");

for my $i (@files){
    @difference=grep { !/^\./ and $i!=$_ } readdir CWD;
}
print "$_\n" for @differenc 

Now the currect dir has this files:

$ls
a.txt  e.txt  getopt.html

With this expression push @files, ("foo.txt", "bar.txt"); the array has these elements: ("foo.txt", "bar.txt", "a.txt", "e.txt", "getopt.html"), which is correct but now I would like to select only those files, which are not in CWD: @difference=grep { !/^\./ and $i!=$_ } readdir CWD;, so I am expecting the array @difference to have again ("foo.txt", "bar.txt") only (I now this has no sense, it's for an example). But the print does not output anything, what is wrong?

1
  • 2
    As you wrote yourself, your example doesn't make much sense, which makes me wonder whether this is not a XY problem. Consider explaining what you are trying to achieve and we might be able to give better answers. Commented Aug 3, 2020 at 12:19

2 Answers 2

5

There are quite a few issues with your code:

  • Your second readdir (in the for my $i (@files) loop) doesn't read anything since the first one (@files=grep{!/^\./}readdir CWD;) already read the whole directory. You could use rewinddir first, but simply using a copy of @files (before pushing foo.txt and bar.txt) would be simpler and more efficient.

  • You are using != instead of ne to compare strings.

  • Every iteration of your loop erases the previous value of @differences since you assign it with =. Presumably push would make more sense.

  • The logic inside your loop is a bit flawed. grep returns the elements that satisfy the condition, but you are more interested in whether any elements satisfies it.

  • You should check if opendir succeeded (by adding a or die ... to the opendir line). Also, note that open my $CWD, getcwd is equivalent to the simpler open my $CWD, ".".

What you probably wanted to do is something like:

use strict;
use warnings;

opendir my $CWD, "." or die "Could not open '.': $!";

my @files = grep{!/^\./} readdir $CWD;
my @init_files = @files;

push @files, ("foo.txt", "bar.txt");

my @difference;
for my $i (@files){
    push @difference, (grep { !/^\./ and $i eq $_ } @init_files) ? () : $i;
}
print "$_\n" for @difference

That's far from efficient however, and more complicated that it needs to be. I suggest instead:

my %files = map { $_ => 1 } grep {!/^\./} readdir $CWD;

my @difference;
for ("foo.txt", "bar.txt") {
    push @difference, $_ if ! exists $files{$_};
}
print "$_\n" for @difference

Note that I've added use strict; use warnings; to the script. Always add them to your codes. While in that specific case, it would not have helped you find out what's wrong, it will save you countless hours in the future. Also, always use lexical file/directory handles (that is, do opendir my $CWD, "dir" rather than open CWD, "dir").

Sign up to request clarification or add additional context in comments.

4 Comments

I use $ only for variables and UPPERCASE for handles. It makes my code cleaner, why to mix them up?
@milanHrabos It does not make you code cleaner. First, it costs you nothing to add a $ in front. Second, if you make a spelling mistake (like CDW instead of CWD), with strict and a $, Perl will issue an error at compile time. Without the $, the line will eventually fail, maybe silently, and you'll have a hard time debugging it. Third, using my $ means that your variable is lexically scoped, which means that you won't have any issues when calling functions for instance.
@milanHrabos For instance, consider sub f { open FH, "<", "f.txt"; $l1 = <FH>; g(); @rest = <FH> } and sub g { open FH, "<", "g.txt"; print for <FH> }. Well, f is not working as you'd expect, since g is opening a file using the same variable as f, thus closing the file opened by f. If you had used my $FH instead, this would not be an issue.
@milanHrabos The idea that the scope of variables should be limited (eg by avoiding global variables (CWD) in favour of properly scoped variables (my $CWD)) is a fundamental tenet of computer science
1

You have two readdir operations on the same filehandle. Once the first reaches EOF, there is nothing more to be returned.

Add a `rewinddir operation before the second iteration.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.