【问题标题】:awk print line as is (with spaces)awk 按原样打印行(带空格)
【发布时间】:2015-06-19 03:16:53
【问题描述】:

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

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 或很长的打印语句,但是文件的列数可能会很麻烦。有没有一种优雅的方式在替换后打印一行?谢谢!

【问题讨论】:

    标签: unix awk text-processing


    【解决方案1】:

    如果你修改了位置参数,Awk 会重新组装该行。但是如果是定宽列的文件,应该可以知道行内哪些位置需要修改,就不需要修改位置参数了。

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

    awk '$4 == "UREA" && $2%2 == 1 { print substr($0, 1, 13) "TH" substr($0, 15) }'
    

    【讨论】:

      【解决方案2】:

      如果您使用的是 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
      

      【讨论】:

        【解决方案3】:

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

        awk '$4=="UREA" && $2%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
        

        【讨论】:

          【解决方案4】:

          修改字段将导致使用 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 处理 gensub()、\S 和 \s。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-06
            • 1970-01-01
            • 2014-09-11
            • 1970-01-01
            • 1970-01-01
            • 2021-08-05
            • 1970-01-01
            相关资源
            最近更新 更多