我正在尝试根据特定条件修改一行,然后打印到一个新文件中。不幸的是,文件必须对列之间的许多空格敏感。典型的行看起来像这样:
ATOM 301 H UREA 24 5.966 3.408 1.877 1.00 0.00 UREA N
这是我使用的命令:
awk '{if ($4 == "UREA" && $2%2 == 1) sub("H","TH",$3);print $0;}' origin.dat > final.dat
基本上,我希望awk打印完全相同的行(具有相同的空格数)但使用替换的第三列。它打印的是:
ATOM 301 TH UREA 24 5.966 3.408 1.877 1.00 0.00 UREA H
我知道我可以使用 printf 或非常长的打印状态,但是文件具有的列数可能很麻烦。替换后是否有一种优雅的方式来打印一条线?谢谢!
答案 0 :(得分:3)
如果修改位置参数,Awk将重新组合线。但如果它是一个具有固定宽度列的文件,您应该能够找出要修改的行中的哪个位置,因此您不需要修改位置参数。
这不是特别优雅,但它保留了你的间距:
awk '$4 == "UREA" && $2%2 == 1 { print substr($0, 1, 13) "TH" substr($0, 15) }'
答案 1 :(得分:3)
如果您使用的是GNU awk
(可能还有其他一些版本),则支持使用固定宽度字段而不是基于分隔符的字段。阅读man awk
以获取更多信息,但您的awk
调用类似于:
awk 'BEGIN{FIELDWIDTHS="10 5 8 3 ..."}{....}'
使用以空格分隔的数字列表在程序开头设置FIELDWIDTHS
变量会导致awk
根据这些值而不是空格(或其他分隔符)拆分每一行...
编辑:以下是使用原始数据的示例,虽然我不得不猜测某些字段宽度,因为问题没有指定它们,而且我是懒得计算它们,假设所输入的内容甚至完全代表了实际数据......我假设所有空格都落后于前一个字段,实际情况可能并非如此...
$ 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"} $4 ~ /^UREA/ && $2 % 2 {sub("H ", "TH", $3); print}'
ATOM 301 TH UREA 24 5.966 3.408 1.877 1.00 0.00 UREA N
答案 2 :(得分:2)
当您访问第三个字段$3
时,原始格式将丢失。以下方法可能会产生不良后果,具体取决于您其他字段中的值,但解决问题的一种方法是在整个记录中执行sub
:
awk '$4=="UREA" && $2%2{sub(/H/,"TH");print}' file
请记住sub
只执行一次替换,因此如果第一列或第二列可以包含" H",则只会产生副作用。例如,根据您的awk版本,您可以使用单词边界使正则表达式更具体。请注意,我使用/H/
作为sub
的第一个参数,而不是"H"
,因为将awk从转换为正则表达式进行了保存。
顺便说一下,由于awk程序的结构为if
,我已删除了condition { action }
的使用情况。我还从您的条件中移除了== 1
,因为数字%
2为真(1)或假(0)。
输出:
ATOM 301 TH UREA 24 5.966 3.408 1.877 1.00 0.00 UREA N
答案 3 :(得分:2)
修改字段将导致使用OFS值作为分隔符重新编译记录。您需要使用RE间隔修改整个记录:
$ awk '$4=="UREA" && $2%2{$0=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 for gensub(),\ S和\ s。