从文件中重复选择特定行

时间:2013-10-04 05:21:48

标签: python perl bash awk

我有一个包含20736行的文件。每条81条线代表分子原子的坐标。所以我有256个分子的总坐标。 现在我想为每个单个分子的特定部分选择坐标。例如,在81个细胞系中,我想从每个分子中选择直到第81个直至所有256个分子的第44行。

为了解释很多细节,我想选择行

44-81 from 1-81 lines
126-163 from 82-163 lines
208-245 from 164-245 lines
290-327 from 246-327 lines
and so on until 20736 lines

为实现这一目标,我尝试使用如下的bash脚本:

#!/bin/bash           

while read line           
do           
echo "$line"           

done < malto-thermo-RT.set30.traj.pdbL1 

但是我不知道如何继续实现一个循环来仅从文件的每个后续81行中选择第44行直到81行。

感谢我得到一些帮助。

我还希望在python,awk和perl中获得解决方案,如果可以用于学习目的。

非常感谢提前。

8 个答案:

答案 0 :(得分:3)

m % n(在许多编程语言中)是“模数”运算符:从n中删除m的所有最大可能整数倍之后剩余的余数。

您要打印的行是模数为81的行号至少为43的行。(如果第一行计为第0行,则效果更好;进行调整意味着您需要编号为43-80的行; 124-161; 205-242等(我认为OP有一个小的算术错误,但它可能是一个解释错误。这里的序列是基于stanzas是81行,如OP所说,而不是82行如例子似乎表明)。

所以,在awk中:

awk  '(NR-1)%81 >= 43' 

这是基于awk的默认操作,即{print},所以我没有提供。{/ p>

编辑:如果OP中提供的示例范围是正确的(例如,如果有一个空白行分隔81行节,那么它们可以更改为:

awk 'NR%82>43'

答案 1 :(得分:1)

这是我使用bash的天真,非惯用的破解:

#!/bin/bash
file=/tmp/file
segment_size=81
select_offset=44
select_size=37

start_line=$select_offset
end_line=$(($start_line + $select_size))

i=0
while read line
do
    i=$(($i + 1))
    if [ $i -ge $start_line ]; then

        [ $i -eq $start_line ] && [ $i != 1 ] && echo -e "\n-------------------\n"

        if [ $i -le $end_line ]; then
            echo "$line"

            if [ $i -eq $end_line ]; then
                start_line=$(($start_line + $segment_size + 1))
                end_line=$(($start_line + $select_size))
            fi
        fi
    fi
done < $file

Bash肯定不是我的强项:\:\似乎工作了!

答案 2 :(得分:1)

由于SO问题的错误,

已修改

使用模数肯定是最好的方法。这个SO问题的最初想法是由@rici添加的!

不幸的是,SO问题是错误的: ...来自82-163行(包括),而 ...来自164-245行,我算了<强> 82 行,而不是81。

首先,我想提供我的 + 备用解决方案。

但现在纠正了,为了更好地匹配SO问题,这可以帮助显示错误的位置:

sed -nf <(for ((i=0;i<20736;i+=82));do echo $((i+44)),$(($i+81))p;done ) < file

bash生成sed命令,sed执行此任务。

分拆说明

bash部分:

for ((i=0;i<20736;i+=82)) ;do
    echo $((i+44)),$(($i+81))p
  done

DO

44,81p
126,163p
208,245p
290,327p
...
20544,20581p
20626,20663p
20708,20745p
  

Nota:这与SO问题样本完全匹配,但不要以20736结束!!

   echo $((20746000/82))
   253000
     

如果它代表分子,则 20736 系中只有252个完整分子。 )

所以sed脚本可以写成:

sed -ne '44,81p;126,163p;208,245p;290,327p;...;20626,20663p;20708,20745p' <file

答案 3 :(得分:1)

perl -ne '
  BEGIN{ ($f,$t)=(44,81) }
  ($.==$f .. $.==$t) =~ /(E0|.)$/ or next;
  print;
  $1 eq "E0" or next;
  $_ += 82 for $f,$t;
' file

答案 4 :(得分:1)

使用@ rici的模数概念简单perl:

perl -ne 'print if $.%82>43' file

答案 5 :(得分:1)

rici使用模数运算符有正确的想法,但随着记录的增加,他的解决方案逐渐变得不同步,如下所示:

$ seq 350 | awk  '(NR-1)%81==43{printf "%i",$0} (NR-1)%81==80{print " -",$0}' 
44 - 81                         # In sync
125 - 162                       # Out of sync by 1 
206 - 243                       # Out of sync by 2 
287 - 324                       # Out of sync by 3 

要打印您要求的行,您将执行以下操作:

$ awk 'NR%82>43' file

打印范围是:

$ seq 350 | awk  'NR%82==44{printf "%i",$0} NR%82==81{print " -",$0}'
44 - 81
126 - 163
208 - 245
290 - 327

用以下方法测试自己:

$ seq 350 | awk  'NR%82>43'

答案 6 :(得分:-1)

你的问题陈述很好,但你没有努力。检查headtail命令的组合方式&amp;如何将参数传递给脚本可以帮助您实现所需的目标。

http://www.ss64.com/bash/head.html
http://www.ss64.com/bash/tail.html

例如,

$ cat file
line1
line2
line3
line4
line5
line6
line7
line8
line9
line10

在此示例中,我们可以使用以下方法打印3到7行:

$ head -7 file | tail -5
line3
line4
line5
line6
line7

答案 7 :(得分:-1)

使用awk,你可以这样做

awk '
    {
    if (NR<=t) 
        {
        for (l=t-37;l<=t;l++) 
            printf "%s ",$l
        }
    if (NR==t)
        {
        t+=82
        }
    } ' t=81 file