cucurbit
cucurbit

Reputation: 1442

Use of uninitialized value in concatenation (.) or string issue

I know that this is very common problem while doing Perl scripts, but I cannot find the solution, and probably it's very simple.

I've two folders inside my data folder: BWA_1 and BWA_2. I want to print the filename with the full path, of a certain file that is inside these folder. To do that, I have this lines inside the script.

my $rpath="/home/seq4/Desktop/data/";

for ( my $i = 1; $i <= 2; $i++ ) {
    my $BWA_dir = $rpath . "BWA_" . $i;
    print "$BWA_dir\n";
    my $bam_file = glob("$BWA_dir/*SNP.bam");
    print "$bam_file\n";
}

When I execute it:

/home/seq4/Desktop/data/BWA_1
/home/seq4/Desktop/data/BWA_1/subset_fusa.SNP.bam
/home/seq4/Desktop/data/
Use of uninitialized value $bam_file in concatenation (.) or string at sc.pl line 17.

How can I fix this problem?

The file exists. If I change this line:

my $bam_file = glob("$BWA_dir/*SNP.bam");

With this (both files have the same name):

my $bam_file = $BWA_dir . "/subset_fusa.SNP.bam";

Output:

    /home/seq4/Desktop/data/BWA_1
    /home/seq4/Desktop/data/BWA_1/subset_fusa.SNP.bam
    /home/seq4/Desktop/data/BWA_2
    /home/seq4/Desktop/data/BWA_2/subset_fusa.SNP.bam

The file exists...:

ls -la BWA_2/
total 8
drwxrwxr-x 2 seq4 alg89 4096 nov 17 17:16 .
drwxrwxr-x 4 seq4 alg89 4096 nov 17 17:15 ..
-rw-rw-r-- 1 seq4 alg89    0 nov 17 17:16 subset_fusa.SNP.bam

Upvotes: 1

Views: 2751

Answers (2)

TLP
TLP

Reputation: 67900

This is described in the documentation perldoc -f glob:

glob EXPR
glob    In list context, returns a (possibly empty) list of filename
        expansions on the value of EXPR such as the standard Unix shell
        /bin/csh would do. In scalar context, glob iterates through such
        filename expansions, returning undef when the list is exhausted.

Note that you are using glob in a scalar context, which in my opinion is the wrong way to use it. In case of multiple matching files, you will only get the first. The natural way to do it would be either using list context with a for loop:

for my $file (glob ...) {
    print "$file\n";
}

Or iterate using a while loop, using scalar context:

while (my $file = glob ...) {

Note also that you can use glob for the whole thing:

my @files = glob "data/BWA_{1,2,3}/*SNP.bam";

(You can use BWA_* for short.)

EDIT:

I have found out the real reason your code does not work. I tried it on my system and I got the same behaviour, even though clearly the files existed.

The reason you are not getting the expected result is like I said early on that you are using glob in scalar context. It is iterating over the results, and after the first, it returns undef. That you changed the variable that is used inside the glob does not matter. By changing 2 characters in your code I got the desired result:

my $rpath = "foo/";

for ( my $i = 1; $i <= 2; $i++ ) {
    my $BWA_dir = $rpath . "BWA_" . $i;
    print "$BWA_dir\n";
    my @bam_file = glob("$BWA_dir/*SNP.bam");
    #  ^--- changed to array to impose list context
    print "@bam_file\n";
}

One might think that using the statement again with a new string would refresh the iterator, but that is not the case. This might be some optimization.

Upvotes: 2

Dan
Dan

Reputation: 10786

You're receiving this warning because $bam_file is undefined, because either there is no file matching /home/Desktop/data/BWA_2/*SNP.bam, or you do not have permission to read that folder in the first place. If you wish to prevent that warning, you can do a few things:

Change the string based on whether a file is found or not:

my $bam_file = glob("$BWA_dir/*SNP.bam") || ''; # If glob returns undef, make $bam_file an empty string

Only print if a file was found:

print "$bam_file\n" if (defined $bam_file);

Upvotes: 1

Related Questions