我有一个包含许多文本文件的文件夹,如下所示:
ATOM 5132 HG22 ILE B 162 -10.906 60.208 9.028 1.00 0.00 H
ATOM 5133 HG23 ILE B 162 -11.193 58.585 9.650 1.00 0.00 H
ATOM 5134 HD11 ILE B 162 -9.888 57.413 9.161 1.00 0.00 H
ATOM 5135 HD12 ILE B 162 -8.448 57.195 8.181 1.00 0.00 H
ATOM 5136 HD13 ILE B 162 -9.913 56.300 7.799 1.00 0.00 H
HETATM 5138 ZN ZN A 190 30.757 32.494 -1.721 1.00 0.00 ZN
HETATM 5139 C1 UQ1 B 501 2.889 33.364 18.810 1.00 0.00 C
HETATM 5140 O1 UQ1 B 501 2.849 32.140 19.037 1.00 0.00 O
HETATM 5141 C2 UQ1 B 501 4.162 33.930 18.303 1.00 0.00 C
HETATM 5142 O2 UQ1 B 501 5.209 33.069 18.099 1.00 0.00 O
HETATM 5143 CM2 UQ1 B 501 5.802 32.349 19.180 1.00 0.00 C
HETATM 5144 C3 UQ1 B 501 4.270 35.396 18.017 1.00 0.00 C
我有一个包含不同数量符号的文件ions_solvents_cofactors
,如下所示:
ZN
008
03S
06C
0KA
0NG
0NM
0QE
144
1CL
1SA
1TP
202
21H
2A6
2BM
2F2
2HE
2HP
2MO
2NO
2PA
2PN
2PO
2T8
我写了一个程序
应该打开并阅读当前文件夹中的每个.txt
文件,并在第1列为ions_solevnts_cofactors
时删除第4列与文件HETATM
中的任何值匹配的行。
它给了我这个错误
rm: cannot remove `ATOM': No such file or directory
rm: cannot remove `1459': No such file or directory
rm: cannot remove `HB': No such file or directory
rm: cannot remove `ILE': No such file or directory
这是脚本
#!/usr/local/bin/perl
use strict;
use warnings;
$dirname = '.';
opendir( DIR, $dirname ) or die "cannot open directory";
@files = grep( /\.txt$/, readdir( DIR ) );
foreach $files ( @files ) {
open( FH, $files ) or die "could not open $files\n";
@file_each = <FH>;
close FH;
close DIR;
my @ion = ();
my $ionfile = 'ions_solvents_cofactors';
open( ION, $ionfile ) or die "Could not open $ionfile, $!";
my @ion = <ION>;
close ION;
for ( my $line = 0; $line <= $#file_each; $line++ ) {
chomp( $file_each[$line] );
if ( $file_each[$line] =~ /^HETATM/ ) {
@is = split '\s+', $file_each[$line];
chomp $is[3];
}
foreach ( $file_each[$line] ) { # line 39
if ( "@ion" =~ $is[3] ) {
system( "rm $file_each[$line]" );
}
}
}
}
我希望脚本覆盖每个文本文件,并只读取以HETATM
开头的第四列。如果它与文件ions_solvents_cofactors
中的任何元素匹配,则应删除此行。
所以,例如
HETATM 5138 ZN ZN A 190 30.757 32.494 -1.721 1.00 0.00 ZN
此行应从文件中删除,因为ZN
匹配。
答案 0 :(得分:1)
有许多必要的改进,以及一些直接错误。
首先是一个简单的工作代码,从问题
中得出一些假设use warnings;
use strict;
use feature 'say';
#use File::Glob ':bsd_glob'; # using \Q..\E in glob, no need for this
use File::Copy qw(move);
use List::MoreUtils qw(any);
my $dirname = shift @ARGV || '.';
my $ionfile = 'ions_solvents_cofactors';
open my $fh, '<', $ionfile or die "Can't open $ionfile: $!";
my @ion_terms = <$fh>;
chomp @ion_terms;
my @files = glob "\Q$dirname\E/*.txt";
foreach my $file (@files) {
open my $fh, '<', $file or do {
warn "Can't open $file: $!";
next;
};
my $outfile = $file . '_new';
open my $fh_out, '>', $outfile or die "Can't open $outfile: $!";
while (<$fh>) {
next if not /^HETATM/;
my @fields = split;
next if any { $fields[3] =~ /$_/ } @ion_terms;
print $fh_out $_;
}
# Uncomment to overwrite, when thoroughly tested
#move $outfile, $file or warn "Can't move $outfile to $file: $!"
}
评论
参考文件只需打开一次;把它弄出循环
无法将数组“初始化”为emtpy,例如my @ion = ()
。当你用my @ion
声明它时,你会得到它。 (如果需要清除数组,则@ary = ();
有意义)
使用词法文件句柄open my $fh, ...
,而不是typeglobs FH
。使用词法文件句柄。使用词法文件句柄。请参阅Typeglobs and Filehandles的结尾并阅读open
几乎不需要C风格的foreach
循环。如果你需要迭代索引,for my $i (0..$#ary)
很棒。但大多数时候你需要元素,比如这里
您应使用\s+
代替split中使用的' '
模式,这也是split
的默认设置。这就是上面的代码不需要它的原因,因为split;
与split ' ', $_;
相同
@file_each
不是文件中行的好名称
直接错误:您正在尝试rm
文件中的行!更好的命名将有助于
您对opendir
和readdir
的使用很好(除了DIR
而不是词法文件句柄!!),但glob
在这里更好。 修改:我在glob
中使用\Q..\E
,以防止可能的注入错误,即异常目录名称触发意外处理。由于这些也是逃避空间,因此不再需要File::Glob及其bsd_glob()
我使用List::MoreUtils::any来查找@ion_terms
中的任何元素是否满足块中的条件,以匹配$fields[3]
。这也可以使用grep
完成。此外,如果您的术语列表与显示的一样短,则可以使用它组装正则表达式模式
my $re = join '|', { quotemeta } @ion_terms; # before the loop
next if $fields[3] =~ /$re/;
上面的一些代码可以更简洁,更简单地编写
答案 1 :(得分:0)
如果我不清楚我对你的建议,我很抱歉
上一个问题
How to delete lines that match elements from another file。我建议你应该发布另一个问题,因为你提出了新的问题,但我打算你应该从我们所处的位置开始工作,而你似乎已经放弃了所有这些并重新开始使用原始代码,包括炮轰rm
错误地认为它会从文件中删除一行
现在您已经显示了ions_solvents_cofactors
的完整版本,我可以看到我的假设是正确的,您提出的唯一其他问题是,只应从PDB中删除以HETATM
开头的行文件,你在你的问题中没有说出来
这与我之前的解决方案非常相似,但我添加了对HETATM
数据的检查。我还改进了日志输出,以便说明来自ions_solvents_cofactors
的哪个值匹配导致删除
请尝试使用此新代码,并在发现任何问题时进行报告
use strict;
use warnings 'all';
use File::Glob ':bsd_glob';
use Tie::File;
my %matches = do {
open my $fh, '<', 'ions_solvents_cofactors';
local $/;
map { $_ => 1 } split ' ', <$fh>;
};
for my $pdb ( glob '*.txt' ) {
tie my @file, 'Tie::File', $pdb or die $!;
for ( my $i = 0; $i < @file; ) {
my ($id, undef, undef, $col4) = split ' ', $file[$i];
if ( $id eq 'HETATM' and $col4 and $matches{$col4} ) {
printf qq{Removing line %d from "%s" (matches %s)\n},
$i+1, $pdb, $col4;
splice @file, $i, 1;
}
else {
++$i;
}
}
}
Removing line 6 from "test.txt" (matches ZN)