更改一行中特定列中的字符串而不会丢失空格/格式

问题描述

我有一个包含多行的文件,但是我希望将我的第5列中前4635行的字符串X更改为另一个字符串A,而不会丢失间的原始制表符/间距列。

我希望更改(针对特定范围的行)

ATOM   2732  HN  SER X 176     181.410 174.270 311.410  0.00  0.00
ATOM   2733  CA  SER X 176     180.170 172.920 310.330  0.00  0.00
ATOM   2734  HA  SER X 176     179.860 171.950 310.720  0.00  0.00
ATOM   2735  CB  SER X 176     179.010 173.910 310.790  0.00  0.00
ATOM   2736  HB1 SER X 176     178.020 173.710 310.340  0.00  0.00
ATOM   2737  HB2 SER X 176     178.910 173.930 311.900  0.00  0.00

进入

ATOM   2732  HN  SER A 176     181.410 174.270 311.410  0.00  0.00
ATOM   2733  CA  SER A 176     180.170 172.920 310.330  0.00  0.00
ATOM   2734  HA  SER A 176     179.860 171.950 310.720  0.00  0.00
ATOM   2735  CB  SER A 176     179.010 173.910 310.790  0.00  0.00
ATOM   2736  HB1 SER A 176     178.020 173.710 310.340  0.00  0.00
ATOM   2737  HB2 SER A 176     178.910 173.930 311.900  0.00  0.00

我想出了以下代码

awk '{if (NR>=1&&NR<=4635) split($0,a,FS,seps); a[5]="A"; for (i=1;i<=NF;i++) printf("%s%s",a[i],seps[i]); print ""}' dat > tmp

,但是似乎文件的所有行现在在第五列中都具有A,而不是第1-4635行。任何建议将不胜感激!

解决方法

使用GNU awk作为match()\s/\S速记的第三个参数:

$ awk 'NR<4636{match($0,/((\S+\s+){4}).(.*)/,a); $0=a[1] "A" a[3]} 1' file
ATOM   2732  HN  SER A 176     181.410 174.270 311.410  0.00  0.00
ATOM   2733  CA  SER A 176     180.170 172.920 310.330  0.00  0.00
ATOM   2734  HA  SER A 176     179.860 171.950 310.720  0.00  0.00
ATOM   2735  CB  SER A 176     179.010 173.910 310.790  0.00  0.00
ATOM   2736  HB1 SER A 176     178.020 173.710 310.340  0.00  0.00
ATOM   2737  HB2 SER A 176     178.910 173.930 311.900  0.00  0.00

或带有任何awk:

$ awk 'NR<4636{match($0,/([^[:space:]]+[[:space:]]+){4}./); $0=substr($0,1,RLENGTH-1) "A" substr($0,RLENGTH+1)} 1' file
ATOM   2732  HN  SER A 176     181.410 174.270 311.410  0.00  0.00
ATOM   2733  CA  SER A 176     180.170 172.920 310.330  0.00  0.00
ATOM   2734  HA  SER A 176     179.860 171.950 310.720  0.00  0.00
ATOM   2735  CB  SER A 176     179.010 173.910 310.790  0.00  0.00
ATOM   2736  HB1 SER A 176     178.020 173.710 310.340  0.00  0.00
ATOM   2737  HB2 SER A 176     178.910 173.930 311.900  0.00  0.00
,

如果输入是固定宽度的字段(如示例中所示),则可以将FIELDWIDTHSGNU awk结合使用:

awk -v FIELDWIDTHS='21 1 *' -v OFS= 'NR<=4635{$2="A"} 1'

在这里,第一个字段由21个字符组成,第二个字段由1个字符组成,其余的则是第三个字段。然后,您只能为必需的行更改第二个字段。


如果输入的宽度不是固定的,则可以使用sedperl

# GNU sed
sed -E '1,4635 s/^((\S+\s+){4})\S+/\1A/'

# if \s and \S isn't supported
sed -E '1,4635 s/^(([^[:space:]]+[[:space:]]+){4})[^[:space:]]+/\1A/'

perl -pe 's/^(\S+\s+){4}\K\S+/A/ if $.<=4635'
,

添加大括号/花括号和一个else分支:

awk '{if (NR>=1&&NR<=4635) {split($0,a,FS,seps); a[5]="A"; for (i=1;i<=NF;i++) printf("%s%s",a[i],seps[i]); print ""} else {print}}' dat > tmp

if的主体没有大括号/大括号,仅包含split命令。