awk 按原样打印行(带空格)

awk print line as is (with spaces)

我正在尝试根据特定条件修改一行,然后打印到一个新文件中。不幸的是,该文件必须对列之间的许多空格敏感。典型的行如下所示:

ATOM     301 H    UREA    24    5.966    3.408    1.877   1.00   0.00 UREA  N

这是我使用的命令:

awk '{if ( == "UREA" && %2 == 1) sub("H","TH",);print [=11=];}' origin.dat > final.dat

基本上,我希望 awk 打印完全相同的行(具有相同数量的空格)但替换了第三列。它打印的是:

ATOM 301 TH UREA 24 5.966 3.408 1.877 1.00 0.00 UREA H

我知道我可以使用 printf 或很长的打印语句,但是文件的列数可能会很麻烦。有没有一种优雅的方法可以在替换后按原样打印一行?谢谢!

如果您修改位置参数,Awk 将重新组装该行。但是如果是定宽列的文件,你应该可以搞清楚行内哪些位置需要修改,所以不需要修改位置参数。

这不是特别优雅,但它保留了您的间距:

awk ' == "UREA" && %2 == 1 { print substr([=10=], 1, 13) "TH" substr([=10=], 15) }'

当您访问第三个字段 </code> 时,原始格式将丢失。根据您其他字段中的值,以下方法可能会产生不良后果,但解决该问题的一种方法是对整个记录执行 <code>sub

awk '=="UREA" && %2{sub(/H/,"TH");print}' file

请记住,sub 只执行一次替换,因此只有在第一列或第二列可以包含 "H" 时才会有副作用。例如,根据您的 awk 版本,您可以使用单词边界使正则表达式更具体。请注意,我使用 /H/ 作为 sub 的第一个参数,而不是 "H",因为这样可以避免 awk 将字符串转换为正则表达式。

顺便说一句,我删除了您对 if 的用法,因为 awk 程序的结构是 condition { action }。我还从您的条件中删除了 == 1 作为数字 % 2 是真 (1) 或假 (0)。

输出:

ATOM     301 TH    UREA    24    5.966    3.408    1.877   1.00   0.00 UREA  N

如果您使用的是 GNU awk(可能还有一些其他版本),则支持使用固定宽度的字段而不是基于定界符的字段。通读 man awk 了解更多信息,但您的 awk 调用看起来像:

awk 'BEGIN{FIELDWIDTHS="10 5 8 3 ..."}{....}'

在程序开头设置 FIELDWIDTHS 变量,使用 space 分隔的数字列表,导致 awk 根据这些值而不是根据这些值拆分每一行spaces(或其他分隔符)...

编辑:这是一个使用原始数据的例子,虽然我不得不猜测一些字段宽度,因为问题没有指定它们,而且我懒得计算它们,假设什么was typed 甚至完全代表了实际数据...我假设所有 spaces 都在前面的字段后面,实际上可能并非如此...

$ echo "ATOM     301 H    UREA    24    5.966    3.408    1.877   1.00   0.00 UREA  N" |\
  awk 'BEGIN{OFS=""; FIELDWIDTHS="9 4 5 8 100"}  ~ /^UREA/ &&  % 2 {sub("H ", "TH", ); print}'
ATOM     301 TH   UREA    24    5.966    3.408    1.877   1.00   0.00 UREA  N

修改字段将导致使用 OFS 值作为分隔符重新编译记录。您需要修改整个记录,而不是使用 RE 间隔:

$ awk '=="UREA" && %2{[=10=]=gensub(/((\S+\s+){2})\S+/,"\1TH","")}1' file
ATOM     301 TH    UREA    24    5.966    3.408    1.877   1.00   0.00 UREA  N

以上使用 GNU awk 进行 gensub()、\S 和 \s。