0

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

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

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 中获得解决方案。

提前谢谢了。

4

8 回答 8

3

m % n(在许多编程语言中)是“模”运算符:nm.

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

所以,在 awk 中:

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

这是基于 awk 的默认操作,即{print},所以我不必提供一个。

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

awk 'NR%82>43'
于 2013-10-04T05:46:14.727 回答
1

Here's my naive, non-idiomatic crack it it using 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 is certainly not my forte :\ :\ Seems to work tho!

于 2013-10-04T06:03:01.587 回答
1
perl -ne '
  BEGIN{ ($f,$t)=(44,81) }
  ($.==$f .. $.==$t) =~ /(E0|.)$/ or next;
  print;
  $1 eq "E0" or next;
  $_ += 82 for $f,$t;
' file
于 2013-10-04T07:06:07.023 回答
1

使用@rici 的模数思想的简单 perl:

perl -ne 'print if $.%82>43' file
于 2013-10-04T07:32:08.960 回答
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'
于 2013-10-04T09:43:00.127 回答
1

由于 SO 问题的错误而进行了编辑。

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

不幸的是,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

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

注意:这完全匹配 SO 问题示例,但不要以 20736 结尾!

   echo $((20746000/82))
   253000

如果它代表分子,那么在20736行中只有 252 个完整的分子。)

所以sed脚本可以写成:

sed -ne '44,81p;126,163p;208,245p;290,327p;...;20626,20663p;20708,20745p' <file
于 2013-10-04T06:57:16.670 回答
-1

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

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
于 2013-10-04T05:33:39.880 回答
-1

使用awk,你可以做一些这样的

awk '
    {
    if (NR<=t) 
        {
        for (l=t-37;l<=t;l++) 
            printf "%s ",$l
        }
    if (NR==t)
        {
        t+=82
        }
    } ' t=81 file
于 2013-10-04T05:52:56.590 回答