如果文件满足两个条件,则重命名该文件

时间:2018-01-23 04:29:20

标签: perl

我正在从当前目录中读取许多pdb个文件,在我专注于特定行的每个文件中,我提出两个条件,如果满足这些条件,那么我试图通过添加重命名该文件从该行到其现有文件名的某些元素。我操作的线条看起来像这样:

HET     CA  A 800       1                                                       
HET    SO4  A 901       5                                                       
HET    SO4  A 902       5                                                       
HET    SO4  A 903       5                                                       
HET    RAM  A 509      11                                                       
HET    GTR  A 510      12                                                       
HET    RAM  A 511      15                                                       
HET    GTR  A 512      12                                                       
HET    RAM  A 513      15                                                       
HET    GTR  A 514      12                                                       
HETNAM      CA CALCIUM ION                                                      
HETNAM     SO4 SULFATE ION                                                      
HETNAM     RAM ALPHA-L-RHAMNOSE                                                 
HETNAM     GTR BETA-D-GALACTOPYRANURONIC ACID                                   
HETSYN     GTR GALACTURONIC ACID                                                
FORMUL   2   CA    CA 2+                                                        
FORMUL   3  SO4    3(O4 S 2-)                                                   
FORMUL   6  RAM    3(C6 H12 O5 N5)                                                 
FORMUL   6  GTR    3(C6 H10 O7)                                                 
FORMUL   7  HOH   *362(H2 O) 
  • 我正在阅读以HETFORMUL开头的行。
  • HET行我正在阅读最后一栏或第四栏。从FORMUL行开始,我正在阅读最后一栏。
  • 如果HET行中的数字为>= 6,而FORMUL下的数字为C >= 2 && sum of O C N>= 6然后我想grep FORMUL行下的第3个元素并将其放入文件名。

例如,从此输入中只有RAM符合这两个条件,因为15下的HETsum of C O N中的=>6 FORMUL所以我期待grep RAM并将其添加到已处理的文件名中。如下所示:filename_RAM如果有更多元素满足这两个条件,则会有更多元素添加到filename

我写的脚本看起来像这样:

#! usr/bin/env perl

use autodie;
use warnings;
use strict;
use File::Glob;

my $pdbs;
my $fh;
my @fh;
my @data;
my $c;
my @lines;
my $lines;
my $file_sz;
my $line;
my @colums;
my $colums;
my @het;
my $het;
my $cols;
my @cols;
my %letters;
my @hetnam;
my $hetnam;

foreach my $pdbs (glob '*pdb') #Reading each pdb file from the current directory
{
    printf "%s\n", $pdbs;
    open my $fh, "<" ,$pdbs;  #Read each pdb file into a filehandle
    #print "$fh\n";
    @lines= <$fh>;            #Putting content of each file into an array
    $file_sz = scalar @lines;
    #print "$file_sz\n";
    #print "@lines\n";

    for ($c=0; $c<=$#lines; $c++) #Reading each line
    {
        chomp ($lines[$c]);
        if ($lines[$c] =~ m/^HET /) #If line starts with HET 
        {   
            my @colums = split ' ', $lines[$c];
            # print join "\t", $colums [4];
            print "$colums[4]\n";       #Printing only a fifth column (4th element)
            push @hetnam, $colums[1];
        }

        if ($lines[$c] =~ m/^FORMUL /) #If line starts with FORMUL
        {   
            my @cols = split /\s+/, $lines[$c], 4;
            next unless $cols[0] eq 'FORMUL';   
            my %letters = $cols[-1] =~ m/([A-Z])(\d+)/g;
            $letters{$lines[$c]} = 0 for (qw[C O N]);
            next if $letters{C} <= 2
              and $letters{C} + $letters{O} + $letters{N} <= 6;
            #print "@cols\n";
        }

        if ($colums[4]=>6 && $letters{C} <= 2 && $letters{C} + $letters{O} + $letters{N} => 6) #line 61
        {
        system ("mv $pdbs $pdbs/_$hetnam");
        }
    }
}

我遇到的错误是:

mv: accessing `4YE1.pdb/_': Not a directory
Use of uninitialized value $letters{"C"} in numeric le (<=) at script1 line 61, <$_[...]> line 5708.
Use of uninitialized value in addition (+) at script1 line 61, <$_[...]> line 5708.

5 个答案:

答案 0 :(得分:2)

  • mv: accessing ``4YE1.pdb/_': Not a directory

    system调用正试图将文件4YE1.pdb移动(并重命名,它永远不会转到)同名文件夹下的文件mkdir,它无法找到。如果您打算创建一个与文件同名的文件夹(我强烈反对),您需要先使用Use of uninitialized value $letters{"C"} in numeric le (<=) at script1 line 61, <$_[...]> line 5708(或等效文件)创建它。

  • %letters

    if所做的更改/分配仅存在于my块的范围内,因为它已使用if进行了本地化。要将其my阻止,请移除my %letters; if ( ... ) { my %letters = ... } if ( ... ) { next if $letters{C} ... }

    所以而不是:

    my %letters;
    if ( ... ) { %letters = ... }
    if ( ... ) { next if $letters{C} ... }
    

    将其写为:

    import re
    def is_valid(x):
        regexp = re.compile(r'CHG00')
        return regexp.search(x) is not None
    df['new_col'] = df['col1'].apply(is_valid)
    

答案 1 :(得分:1)

您的代码没有尝试实现您描述的逻辑的关键部分,并且在其他地方存在各种小的逻辑错误。这是尝试将其重构为合理惯用的Perl,同时保留尽可能多的原始内容。

我遗漏了实际的重命名功能,并留下了一些调试打印件,以帮助您了解如何在开发过程中尝试帮助自己查看程序正在执行的操作。

#! usr/bin/env perl

use autodie;
use warnings;
use strict;

foreach my $pdb (<*pdb>)
{
    printf "# %s\n", $pdb;
    open my $fh, "<" ,$pdb;
    my %hets;
    my @suf;
    # Don't slurp the entire file. Just read a line at a time.
    for my $line (<$fh>)
    {
        chomp ($line);
        if ($line =~ m/^HET /)
        {   
            my @columns = split ' ', $line;
            ####print "$columns[4] < 6? ", ($columns[4] < 6 ? "yes" : "no"), "\n";
            next if $columns[4] < 6;
            # We have a HIT, er, HET -- remember it
            $hets{$columns[1]} = $columns[4];
            ####print "$columns[1] added to hets\n";
        }
        elsif ($line =~ m/^FORMUL /)
        {
            my @cols = split /\s+/, $line, 4;
            # If this is not in $hets, skip it
            ####print "$cols[2] in %hets? ", ($hets{$cols[2]} ? "yes" : "no"), "\n";
            next unless $hets{$cols[2]};
            ####print "\$cols[-1] is $cols[-1]\n";
            # Initialize these to zero _before_ extracting actual counts
            my %letters = (C=>0, O=>0, N=>0);
            while ($cols[-1] =~ m/([CON])(\d+)/g)
            {
                $letters{$1} = $2;
                ####print "\$letters{$1} = '$2'\n"
            }
            my $con = $letters{"C"} + $letters{"O"} + $letters{"N"};
            # Bug fix: next condition was wrong
            # ("next if this OR that" is equivalent to "don't next if this AND that")
            if ($letters{"C"}>2 && $con >= 6)
            {
                push @suf, $cols[2]
            }
        }
    }
    if (@suf)
    {
        print "rename $pdb, ", join("_", $pdb, @suf), "\n";
    }
    # my %hets and my @suf go out of scope here --
    # helps you find bugs and make sure you don't process old results
    # from a previous file
}

使用my所有变量声明为有效全局my的原因。变量应该在您实际使用它的范围内声明,然后在它超出范围时被遗忘。这对于初学者来说很难,但是可以帮助你排队,因为你不能通过在不同的地方为不同的事物使用相同的变量来绊倒你自己的脚,或者对同一循环的前一次迭代中的旧值进行操作(值将会从undef声明中my,直到您实际给它一个值)。当然,在封闭的范围内再次使用my声明全局变量然后再次使用my重新声明它们不仅毫无意义,而且确实完全令人困惑。

除了实际实现您的要求中的许多条件而不是代码中的条件之外,这里的主要变化是在处理文件中的行时收集匹配的后缀,然后只有在我们处理完后才尝试重命名它们整个文件。

@Borodin更新

我修改了代码,希望在评论中解释我的想法。语义是相同的:这不是重构,我不知道逻辑是否正确。

use strict;
use warnings;
use autodie;

for my $pdb ( glob '*pdb' ) {

    printf "# %s\n", $pdb;

    open my $fh, "<", $pdb;

    my %hets;
    my @suf;

    for my $line ( <$fh> ) {

        chomp( $line );

        if ( $line =~ m/^HET / ) {
            my @columns = split ' ', $line;
            next if $columns[4] < 6;

            $hets{ $columns[1] } = $columns[4];
        }
        elsif ( $line =~ m/^FORMUL / ) {

            my @cols = split /\s+/, $line, 4;

            next unless $hets{ $cols[2] };

            my %letters = ( C => 0, O => 0, N => 0 );

            while ( $cols[-1] =~ m/([CON])(\d+)/g ) {
                $letters{$1} = $2;
            }

            my $con = $letters{"C"} + $letters{"O"} + $letters{"N"};

            if ( $letters{"C"} > 2 && $con >= 6 ) {
                push @suf, $cols[2];
            }
        }
    }

    if ( @suf ) {
        print "rename $pdb, ", join( "_", $pdb, @suf ), "\n";
    }
}

答案 2 :(得分:0)

可能是系统调用中的shell转义,或者是如何设置%letters的错误。

使用内置的rename或核心模块File::Copy可能会有更好的成功。您还可以将系统调用更改为3个参数,一个用于进程mv,另一个用于源和目标。但我认为你应该只使用前两个选项。

等等,我发现你正试图从$pdbs转到$pdbs/_$hetname。这将是棘手的,因为文件存在,然后你尝试将它移动到一个有自己名字的路径,一个目录应该是什么?这将需要多个步骤,例如:

rename($pdbs, "$pdbs_$hetname");
mkdir($pdbs);
rename("$pdbs_$hetname", "$pdbs/_$hetname");

除了我希望每个$pdbs都是唯一的,否则这将在下一次失败。

答案 3 :(得分:0)

我无法确定修复,因为您还没有详细描述您的代码应该做什么。但FORMUL的代码块没有意义,我认为它应该是这样的

if ( $lines[$c] =~ m/^FORMUL\b/ ) {
    my @cols = split ' ', $lines[$c], 4;
    my %letters = ( C => 0, O => 0, N => 0 );

    $letters{$1} = $2 while $cols[-1] =~ m/([A-Z])(\d+)/g;

    next if $letters{C} <= 2
        and $letters{C} + $letters{O} + $letters{N} <= 6;
}

另请注意您的移动操作

system ("mv $pdbs $pdbs/_$hetnam")

要求系统创建一个与已存在的文件同名的目录,这将失败。您可以使用tempdir中的File::Temp

分三步完成此操作
use File:;Temp 'tempdir';

my $newdir = tempdir;
rename $pdbs, "$newdir/_$hetnam";
rename $newdir, $pdbs;

但请注意,您从未为$hetnam

设置值

根据底层平台,您可能需要将rename替换为move来自File::Copy

答案 4 :(得分:0)

这是一个快速尝试重新实现你在shell + Awk中描述的内容。

#!/bin/bash

for file in *pdb; do
    if suffixes=$(awk 'BEGIN { suf = "" }
        # If first column is HET and number >= 6, remember this one
        $1 == "HET" && $5 >= 6 { het[$2] = $5 }
        # If first column is FORMUL and this is a HET we remembered ...
        $1 == "FORMUL" && ($3 in het) {
           # If there are parentheses, trim them and anything outside
           sub(/^.*\(/, "", $4);
           sub(/\).*/, "", $NF);
           # Now sum O, C, and N entries, but abort if C <= 2 or missing
           sum = 0
           c = 0
           for(i=4; i<=NF; ++i) {
             if ($i ~ /^[OCN]/) {
               n = 0 + substr($i, 2)
               if ($i ~ /^C/} {
                 if (n <= 2) next;
                 c=1;
               }
               sum += n
             }
           }
           # If we did not see any C, abort this line
           if (!c) next;
           # Now if sum >= 6, add suffix
           if (sum >= 6) suf = suf "_" $3 
        }
        # We are done. Print result if any.
        # Otherwise "exit 1" will cause the calling "if" to fail.
        END { if (suf) { print suf; exit 0 } exit 1 }' "$file")
    then
        mv "$file" "$file$suffixes"
    fi
done

这会为您的测试数据生成后缀_RAM_GTR,如果您的标准正确明确,我不明白为什么不应包含GTR

我将从评论中重申,您遇到的一个错误是您在目标文件名中的下划线之前错误添加的斜杠。你无法将文件重命名为目录名,这就是斜杠在Unix文件名中指定的内容。