【问题标题】:Use of uninitialized value in concatenation (.) or string issue在连接 (.) 或字符串问题中使用未初始化的值
【发布时间】:2017-06-23 02:29:58
【问题描述】:

我知道这是在编写 Perl 脚本时很常见的问题,但我找不到解决方案,而且可能很简单。

我的数据文件夹中有两个文件夹:BWA_1 和 BWA_2。我想打印这些文件夹中某个文件的完整路径的文件名。为此,我在脚本中有这些行。

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";
}

当我执行它时:

/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.

我该如何解决这个问题?

文件存在。如果我改变这一行:

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

有了这个(两个文件同名):

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

输出:

    /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

文件存在...:

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

【问题讨论】:

  • 请注意,如果您不使用通配符,glob 将打印文件名,即使它不匹配。请改用ls 查看您的文件。
  • 如果 BWA_2 中有一个具有该名称的文件,并且您拥有完全权限,那么我认为我们无法复制此内容。也许告诉我们print glob('/home/Desktop/data/BWA_2/*SNP.bam')ls -la /home/Desktop/data/BWA_2 的输出。
  • 您的编辑并不能证明第二个文件存在。您只是打印一个字符串,而不是检查文件是否存在。如果添加print "$bam_file exists: ", -f $bam_file, "\n"; 之类的代码,您会看到什么?
  • 在 sc.pl 第 13 行的串联 (.) 或字符串中使用未初始化的值 $bam_file。在 sc.pl 第 13 行的 -f 中使用未初始化的值 $bam_file。在打印中使用未初始化的值在 sc.pl 第 13 行。存在:
  • 它似乎不存在..但是...为什么第一个文件是的?我的意思是,我已经创建了这两个文件,只需输入: touch BWA_1/subset_fusa.SNP.bam BWA_2/subset_fusa.SNP.bam

标签: perl


【解决方案1】:

这在文档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.

请注意,您在标量上下文中使用glob,我认为这是错误的使用方式。如果有多个匹配文件,您只会得到第一个。这样做的自然方法是使用带有 for 循环的列表上下文:

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

或者使用一个while循环进行迭代,使用标量上下文:

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

另请注意,您可以使用glob 来处理整个事情:

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

(您可以简称为BWA_*。)

编辑:

我找到了您的代码不起作用的真正原因。我在我的系统上尝试了它,我得到了相同的行为,即使文件显然存在。

您没有得到预期结果的原因就像我早先所说的那样,您在标量上下文中使用glob。它对结果进行迭代,在第一次之后,它返回 undef。您更改了 glob 内部使用的变量并不重要。通过更改代码中的 2 个字符,我得到了想要的结果:

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";
}

有人可能认为再次使用带有新字符串的语句会刷新迭代器,但事实并非如此。这可能是一些优化。

【讨论】:

  • 感谢 TLP。我知道使用 glob 的正确方法是使用数组,但是,我确信文件夹中只有一个具有该名称的文件,这就是我使用它的原因。无论如何,如果您知道更好的方法来做到这一点,我会很高兴阅读它。我已经编辑了帖子。您可以看到我要查找的文件的完整名称。
  • @cucurbit 当 Perl 说文件不存在而你说它存在时,通常是你错了。如果 Perl 错了,那无论如何都是你的错。您对文件是否确实存在的测试并不确定,因为即使foo 不存在,glob "foo" 也会返回foo。使用ls检查文件是否存在。
  • @cucurbit 你能通过手动检查确认文件在那里吗?
  • 也可以这样做:my ($bam_file) = glob("$BWA_dir/*SNP.bam"); 这也将强制列表上下文。数组的第一个元素将被分配,其余元素将被丢弃。
  • @Sobrique 是的,这对于这种特定情况很有用,每个目录只有一个文件。但是,显示所有结果更正确,否则这将成为未来代码扩展中很难发现的错误。
【解决方案2】:

您收到此警告是因为 $bam_file 未定义,或者没有与 /home/Desktop/data/BWA_2/*SNP.bam 匹配的文件,或者您一开始就没有读取该文件夹的权限。如果您希望阻止该警告,您可以执行以下操作:

根据是否找到文件更改字符串:

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

仅在找到文件时打印:

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

【讨论】:

  • 我知道为什么我会收到警告...问题是该文件存在...实际上我需要将该文件存储在变量中,而不仅仅是避免警告。谢谢!
猜你喜欢
  • 1970-01-01
  • 2014-05-20
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 2017-04-27
  • 2022-11-04
相关资源
最近更新 更多