仅编辑文本文件中遵循模式的那些行?

时间:2015-04-11 21:35:01

标签: bash text awk grep

我有一个表格的文本文件:

示例输入文件

atomic structure.
created 8/4/15. author: becko
molecule name here
 O    0.000000 0    0.000000 0    0.000000 0       0    0    0
 C    1.422713 1    0.000000 0    0.000000 0       1    0    0
 C    1.536633 1  109.666084 1    0.000000 0       2    1    0
 C    1.523232 1  110.673515 1   53.747574 1       3    2    1
 C    1.524902 1  110.675377 1  -51.051605 1       4    3    2
 C    1.524624 1  110.815956 1   53.399712 1       5    4    3
 O    1.388625 1  108.653427 1  -68.335587 1       2    3    4
 O    1.418326 1  111.098351 1   58.126965 1       3    2    7
 O    1.429752 1  106.981445 1 -172.599930 1       4    3    2
 O    1.431727 1  110.929413 1  171.804962 1       5    4    3
 C    1.389881 1  117.191086 1   95.674500 0      10    5    4
 C    1.529863 1  107.679131 1  146.326675 0      11   10    5
 C    1.524202 1  110.428741 1  170.992218 1      12   11   10

格式为:三个初始标题行,包含标题,作者等,后跟一个表,包含10列和任意数量的行。第一列是文本(通常是单个字符),第2,4和6列包含十进制数,其余列是非负整数。

我需要一个命令来查找包含的所有行,例如,第8列中的3和第9列中的2。该命令应返回此模式后面的行号列表。我怎样才能在bash脚本中执行此操作?我想将行号列表分配给变量(如lines=7 11),以便我可以稍后在脚本中循环遍历其内容。

修改:按照@shelter的建议,我会发布完整的问题。 我需要查找包含的所有行,例如,第8列中的3和第9列中的2。然后我需要在所有这些行的第6列添加/减去一个固定数字,比如说3.4。我怎么能这样做?

鉴于前面的示例输入文件,我希望得到以下输出文件:

示例输出

atomic structure.
created 8/4/15. author: becko
molecule name here
 O    0.000000 0    0.000000 0    0.000000 0       0    0    0
 C    1.422713 1    0.000000 0    0.000000 0       1    0    0
 C    1.536633 1  109.666084 1    0.000000 0       2    1    0
 C    1.523232 1  110.673515 1   57.147574 1       3    2    1
 C    1.524902 1  110.675377 1  -51.051605 1       4    3    2
 C    1.524624 1  110.815956 1   53.399712 1       5    4    3
 O    1.388625 1  108.653427 1  -68.335587 1       2    3    4
 O    1.418326 1  111.098351 1   61.526965 1       3    2    7
 O    1.429752 1  106.981445 1 -172.599930 1       4    3    2
 O    1.431727 1  110.929413 1  171.804962 1       5    4    3
 C    1.389881 1  117.191086 1   95.674500 0      10    5    4
 C    1.529863 1  107.679131 1  146.326675 0      11   10    5
 C    1.524202 1  110.428741 1  170.992218 1      12   11   10

2 个答案:

答案 0 :(得分:2)

简单的awk脚本:

awk '$8==3 && $9==2{print NR}'

每行会产生一个数字,但你仍然可以分配一个变量:

lines=$(awk '$8==3 && $9==2{print NR}' file.tsv)

或者,使用bash,作为数组:

lines=($(awk '$8==3 && $9==2{print NR}' file.tsv))

如果你真的希望所有数字都在一行上:

awk '$8==3 && $9==2{printf "%d ",NR}'

答案 1 :(得分:1)

假设col8col9add是bash变量,分别包含要在第8列和第9列中匹配的值以及要添加到第6列的值, (例如col8=3 col9=2 add=3.4)尝试:

awk '$8==a && $9==b{$6+=c}1' a=$col8 b=$col9 c=$add input-file

请注意,这会略微修改输出的间距。使输出均匀(但仍然不同于原始)的最简单方法可能是:

awk '$8==a && $9==b{$6+=c}{$1=$1}1' a=$col8 b=$col9 c=$add input-file

更重要的是,间距问题(您可以使用printf解决)是数据的精确度,因此您可能希望:

awk '$8==a && $9==b{$6+=c}1' CONVFMT=%0.9g  a=$col8 b=$col9 c=$add input-file

这只是将shell变量col8,col9和add添加到awk变量a,b和c中,然后遍历文件的行。当列匹配时(规则$8==a && $9==b的计算结果为true),将执行算术运算。 1导致每行打印。