【问题标题】:Merge files having same prefix from different directories合并来自不同目录的具有相同前缀的文件
【发布时间】:2018-08-30 21:43:13
【问题描述】:

我编写了以下脚本:

#!/usr/bin/perl -w

use strict;

die "usage:$0 <Input_folder_1>\t<Input_folder_2>\t<Out_folder>\t<Project_name>\t\n" unless $#ARGV == 3;

my $folder1 = shift;
#print "$folder1\n";
my $folder2 = shift;
#print "$folder2\n";
my $out = shift;
my $project_name = shift;

my $file1;
my $file2;
my $file3;
my $file4;

#print "$project_name\n";
foreach(glob("$folder1/$project_name\_S[0-9]_R1_001.fastq.gz")){
    chomp;
    #print "Hello World\n";
    $_ =~ m{$folder1/$project_name\_S[0-9]_R1_001\.fastq.gz};
        #print "$_\n";
        $file1 = $_;
        print "$file1\n";
}

foreach(glob("$folder2/$project_name\_S[0-9]_R1_001.fastq.gz")){
    chomp;
    #print "Hello World\n";
    $_ =~ m{$folder2/$project_name\_S[0-9]_R1_001\.fastq.gz};
        #print "$_\n";
        $file2 = $_;
        print "$file2\n";
}

cat $file1 $file2 &gt; $out/$project_name.R1.fastq.gz; #第 42 行

foreach(glob("$folder1/$project_name\_S[0-9]_R2_001.fastq.gz")){
    chomp;
    #print "Hello World\n";
    $_ =~ m{$folder1/$project_name\_S[0-9]_R2_001\.fastq.gz};
        #print "$_\n";
        $file3 = $_;
        print "$file3\n";
}

foreach(glob("$folder2/$project_name\_S[0-9]_R2_001.fastq.gz")){
    chomp;
    #print "Hello World\n";
    $_ =~ m{$folder2/$project_name\_S[0-9]_R2_001\.fastq.gz};
        #print "$_\n";
        $file4 = $_;
        print "$file4\n";
}

`cat $file3 $file4 > $out/$project_name.R2.fastq.gz`;

这个脚本运行如下:

./script.pl folder1 folder2 output_folder project_name

当我使用以下文件运行此脚本时,它运行顺利

folder1/123-abcQ_S3_R1_001.fastq.gz
folder2/123-abcQ_S1_R1_001.fastq.gz
folder1/123-abcQ_S3_R2_001.fastq.gz
folder2/123-abcQ_S1_R2_001.fastq.gz

./script.pl folder1 folder2 out/ 123-abcQ 

它将文件夹1/123-abcQ_S3_R1_001.fastq.gz和文件夹2/123-abcQ_S1_R1_001.fastq.gz合并,在输出目录中创建一个合并的123-abcQ.R1.fastq.gz文件。

但是当我使用以下文件运行相同的脚本时,它给了我一个错误:

folder1/demo-1_S10_R1_001.fastq.gz
folder1/demo-1_S10_R2_001.fastq.gz
folder2/demo-1_S12_R1_001.fastq.gz
folder2/demo-1_S12_R2_001.fastq.gz

./script.pl folder1 folder2 out/ demo-1

在连接 (.) 或字符串中使用未初始化的值 $file1 ./script.pl 第 42 行。使用未初始化的值 $file2 in ./script.pl 第 42 行的连接 (.) 或字符串。

我不知道如何解决这个问题。您的建议将不胜感激。

【问题讨论】:

  • 请为我们标记第 42 行。
  • S[0-9]_ 无法匹配S10_,有两位数,不是一位。
  • 嗨,当我用 [0-9]+ 修改正则表达式时,它仍然给出了同样的错误。与 + 运算符匹配一个或多个数字。

标签: regex perl


【解决方案1】:

使用您的第二组参数,您不应该在警告之前看到任何输出。它不打印任何东西。

这是因为它不会从您的 glob 调用中返回任何文件,因此 foreach 循环实际上不会被执行。 $file1 最初是 undef,现在永远不会被设置。

my $file1; # starts out as undef
# ...

#print "$project_name\n";
foreach(glob("$folder1/$project_name\_S[0-9]_R1_001.fastq.gz")){ # finds nothing
    chomp;
    #print "Hello World\n";
    $_ =~ m{$folder1/$project_name\_S[0-9]_R1_001\.fastq.gz};
        #print "$_\n";
        $file1 = $_;
        print "$file1\n"; # no output here
}

它可能找不到您的文件,因为您没有任何与该模式匹配的文件。

这里发生了两件事:

  • glob 接受 a sort of pattern 可以包含 wildcards。它只返回与此模式匹配的文件。它**不是正则表达式*。
  • 您正在执行模式匹配,并忽略结果。

让我们更仔细地看一下。

foreach(glob("$folder1/$project_name\_S[0-9]_R2_001.fastq.gz")){

有趣的是glob EXPR。你的表达是:

# | variable interpolation 
# |        | variable interpolation            
# |        |            | treat this as a literal underscore, not part of var name
# |        |            |  one digit out of group 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
# |        |            |  |   |
  $folder1/$project_name\_S[0-9]_R2_001.fastq.gz

这将返回与此模式匹配的文件列表。如果它没有找到文件,则不返回任何内容。 foreach 循环然后迭代该列表。同样,如果列表中没有任何内容,则永远不会调用循环。

foreach ( glob ... ) {
    chomp;
    $_ =~ m{$folder1/$project_name\_S[0-9]_R1_001\.fastq.gz};
    $file1 = $_;

您现在用chomp 切断了一个换行符。这没有任何意义,因为文件名的末尾通常没有换行符。

然后,您使用与 glob 相同的模式对文件名进行模式匹配。在这种情况下,它是一个实际的正则表达式,因此某些字符具有特殊含义。

m{
  $folder1        # variable interpolation
  /               # literal slash /
  $project_name   # variable interpolation
  \_S             # literal backslash \ and S
  [0-9]           # one digit from 0 to 9
  _R1_001         # literal string
  \.              # literal dot .
  fastq           # literal string
  .               # exactly one of any character
  gz              # literal string
};

如您所见,该模式意味着完全不同的东西。您已经逃脱了. 中的一个点,但没有同时逃脱。

不过没关系,因为这个操作不做任何事情。 你只是把结果扔掉!

然后将$_ 分配给$file1,无论是否匹配。


我认为只获取该目录中的所有 gzip 压缩文件然后检查它们会更有意义。

foreach my $filename ( glob <$folder1/${project_name}*.fastq.gz> ) {
    if ( $filename =~ m{
            /             # separates the folder from the filename
            $project_name # anchor to project
            _         
            [0-9]+        # one or more numbers (001, 123, 9, ...)
            _R1_001 
            \.fastq\.gz   # file type
            $             # end of string
        }x
     ) {
        $file1 = $filename;
        last;
    }
}

这使用了另一种glob 语法,我发现它更具可读性,获取$folder1 中以$project_name 开头并以.fastq.gz 结尾的所有文件。然后它会迭代文件列表并执行模式匹配以确保我们确实获得了正确的文件。我已经包含 /x 修饰符以忽略模式中的空格,因此我们可以使用 cmets。

注意[0-9]+,它表示一个或多个数字。这很重要,因此可以找到数字大于 9 的文件。

一旦找到匹配项,它就会分配$file1,然后以last 退出循环。

您可能还想在运行使用$file1$file2 的外部命令之前添加一个检查。

if ($file1 && $file2) {
     `cat $file1 $file2 > $out/$project_name.R1.fastq.gz`
} else {
     print "No matches found for first set of files.";
}

【讨论】:

  • 那么为什么它适用于其他文件?你能告诉我如何解决这个问题吗?
  • @Callie 我无法回答这个问题,因为我不知道你那里有什么文件。首先查看您的$folder1 目录并列出其中的所有文件,然后查看它们是否与您的模式匹配。也许获取该文件夹中的所有文件并仅使用模式匹配所需的文件是一个更好的主意。当然更容易调试。
  • 我尝试在我正在使用的正则表达式中使用不同的表达式。例如 [0-9][0-9] 将匹配 demo-1_S10_R1_001.fastq.gz,但对于 sampleA_S2_R1_001.fastq.gz 将失败。我需要修改 foreach(glob("$folder1/$project_name_S[0-9]_R1_001.fastq.gz 以便它也适用于 S10 和 S2。我尝试过 [0-9]+ 和 [0-9 ][0-9]?但都失败了。你能建议任何修改吗?
  • 我将正则表达式修改为 [0-9]*,它也适用于 S10 和 S2。
  • @Callie 你没有使用正则表达式。您正在为glob 使用通配符。这是两个根本不同的东西,只是碰巧共享一些相似的语法。 [0-9]*glob 的模式中并不意味着很多数字,但至少没有,它的意思是一个数字,后面跟着任何东西,可能有很多东西或什么都没有.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-21
  • 2022-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多