【问题标题】:Print a block of text until the first blank line using awk使用 awk 打印一段文本直到第一个空行
【发布时间】:2016-01-23 02:55:46
【问题描述】:

这是我的示例文件:

Host dns2
        HostName 172.20.4.80
        User root
        Port 22

Host dns1
        HostName 172.20.4.75
        User root
        Port 22

Host dns3
        HostName 172.20.4.76
        User root
        Port 22

Host dns4
        HostName 172.20.4.77
        User root
        Port 22

Host dns5
        HostName 172.20.4.78
        User root
        Port 22

Host dns6
        HostName 172.20.4.79
        User root
        Port 22

我只想打印一个块意味着例如

Host dns1
        HostName 172.20.4.75
        User root
        Port 22

输出:

Host: dns2  HostName: 172.20.4.80   User: root  Port: 22

但在此示例中,所有块都有 4 行,可能它们稍后会达到 5 行或更多行,所以我想从主机打印到第一个空行或从主机删除到第一个空行

我真的不擅长正则表达式,需要这个来完成我的脚本

谢谢

【问题讨论】:

  • 你想匹配每个块吗? ^Host\sdns\d+(?:\s+\w+\s[\w\.]+)+
  • 谢谢我添加我想要的输出

标签: regex awk sed


【解决方案1】:

我认为你基本上想要这个:

awk -v RS='' '/dns1/' file

取消设置记录分隔符,以便将每个块视为一条记录,然后打印与该模式匹配的记录。

或者使用shell变量:

host=dns1
awk -v host="$host" -v RS='' '$0 ~ host' file

在这两个示例中,我使用的默认操作是{ print }。由于您可能会使用{ printf ... } 更改输出,因此您可能需要考虑添加exit 语句以避免不必要地处理文件的其余部分。

【讨论】:

  • 谢谢它的工作,但你能修改命令以显示我想要的输出吗?
  • 您可以在 awk 脚本的末尾添加一个类似 { printf "%s %s", $1, $2 } 的块 - 试一试。
  • 我用过 { print ($1~/^Host$/ ? "" : "\t") $1 ": " $2} 但不幸的是,在使用 { printf "%s %s", $1, $2 } 你能帮忙给我一个 sed 命令来删除吗?
  • 您也不需要使用 sed。我给您的示例仅打印前两个字段 $1$2 - 如果您想要更多字段,请在格式说明符中添加更多 %s,然后将 $3、$4` 等添加到参数列表。
  • 为了效率:awk -v RS= '/dns1/ {print whatever; quit}' file
【解决方案2】:

类似的awk

$ awk -v RS= -v OFS=' ' '{for(i=1;i<NF;i+=2) $i=$i":"}1' hosts

Host: dns2 HostName: 172.20.4.80 User: root Port: 22
Host: dns1 HostName: 172.20.4.75 User: root Port: 22
Host: dns3 HostName: 172.20.4.76 User: root Port: 22
Host: dns4 HostName: 172.20.4.77 User: root Port: 22
Host: dns5 HostName: 172.20.4.78 User: root Port: 22
Host: dns6 HostName: 172.20.4.79 User: root Port: 22

将以所需的输出格式为您提供所有记录。您可以进一步过滤此输出或添加诸如

之类的模式
$ awk -v RS= -v OFS=' ' '{for(i=1;i<NF;i+=2) $i=$i":"} /dns2/' hosts

Host: dns2 HostName: 172.20.4.80 User: root Port: 22

如果要在处理完选中的记录后退出,需要稍微修改一下脚本

$ awk -v RS= -v OFS=' ' '/dns2/{for(i=1;i<NF;i+=2) $i=$i":"; print; exit}' hosts
Host: dns2 HostName: 172.20.4.80 User: root Port: 22

如果您想选择除一条记录之外的所有内容,您可以否定模式(并删除退出)

$ awk -v RS= -v OFS=' ' '!/dns2/{for(i=1;i<NF;i+=2) $i=$i":"; print}' hosts
Host: dns1 HostName: 172.20.4.75 User: root Port: 22
Host: dns3 HostName: 172.20.4.76 User: root Port: 22
Host: dns4 HostName: 172.20.4.77 User: root Port: 22
Host: dns5 HostName: 172.20.4.78 User: root Port: 22
Host: dns6 HostName: 172.20.4.79 User: root Port: 22

请注意,sed 就地替换需要一个中间文件。如果你想用格式化的无记录的文件替换原始文件,你可以在最后一个 awk 语句中使用这个命令模式

$ awk ... > temp && mv temp original

更新: 设置 OFS 将更改字段之间的所有分隔符。您想按name: value 对它们进行逻辑分组,因此请更改脚本

$ awk -v RS= '{for(i=1;i<NF;i++) $i=$i (i%2?":":"\t")}1' hosts
Host: dns2       HostName: 172.20.4.80   User: root      Port: 22
Host: dns1       HostName: 172.20.4.75   User: root      Port: 22
Host: dns3       HostName: 172.20.4.76   User: root      Port: 22
Host: dns4       HostName: 172.20.4.77   User: root      Port: 22
Host: dns5       HostName: 172.20.4.78   User: root      Port: 22
Host: dns6       HostName: 172.20.4.79   User: root      Port: 22

在甚至定位的字段之后设置制表符分隔符。

【讨论】:

  • 非常感谢你能不能给一个sed命令来删除这个块?例如删除 dns2
  • 你不需要sed,有了awk你可以很容易的选择剩下的记录。查看更新。
  • 你的意思是我使用 awk 选择除该行之外的所有内容并再次重定向到该文件?
  • 如果我想在项目之间添加一个标签,我该怎么办?主机:dns2 主机名:172.20.4.80 用户:root 端口:22
  • 您的替换不起作用,因为您将输出发送到原始文件,它不是旧文件,它是一个新文件,主机:dns1 主机名:172.20.4.75 用户:root 端口:22 不是主机 dns1 主机名 172.20.4.75 用户根端口 22 所以仍然 sed 我认为它是最好的解决方案
【解决方案3】:

要打印第三条记录:

$ awk -v RS= -F'\n[[:blank:]]+' -v OFS='\t' 'NR==3{$1=$1; gsub(/ +/,": "); print}' file
Host: dns3      HostName: 172.20.4.76   User: root      Port: 22

打印包含dns4的记录:

$ awk -v RS= -F'\n[[:blank:]]+' -v OFS='\t' '/dns4/{$1=$1; gsub(/ +/,": "); print}' file
Host: dns4      HostName: 172.20.4.77   User: root      Port: 22

要打印除包含dns3dns4dns5 的记录之外的所有记录:

$ awk -v RS= -F'\n[[:blank:]]+' -v OFS='\t' '!/dns[345]/{$1=$1; gsub(/ +/,": "); print}' file
Host: dns2      HostName: 172.20.4.80   User: root      Port: 22
Host: dns1      HostName: 172.20.4.75   User: root      Port: 22
Host: dns6      HostName: 172.20.4.79   User: root      Port: 22

【讨论】:

    【解决方案4】:

    与 Tom Fenech 方法没有什么不同,因为它使用记录分隔符,但它也使用字段分隔符来获得所需的输出:

    awk -v RS='' -F'\n[\t ]*' -v OFS='  ' '/dns1/{$1=$1;print}' file
    

    当你更改输出字段分隔符时,你需要使用$1=$1(或$0=$0或任何其他字段)强制awk重新评估记录并考虑新的字段分隔符。

    注意:当使用exit 命令找到匹配块时,您可以退出 awk。这避免了处理文件的所有结尾。 您也可以只使用第一个字段测试模式/dns1/

    awk -v RS='' -F'\n[\t ]*' -v OFS='  ' '$1~/dns1/{$1=$1;print;exit}' file
    

    如果您在结果中添加分号,由于您修改了字段,$1=$1 技巧变得无用。你可以写:

    awk -v RS='' -F'\n[\t ]*' -v OFS='  ' '$1~/dns1/{for(i=1;i<=NF;i++){sub(" ", ": ", $i)};print;exit}' file
    

    【讨论】:

    • 非常感谢它是迄今为止最接近的答案,如果我想用我想要的输出打印所有行,我可以做两件事?第二个你能给我 sed 命令删除块吗?
    • 仅供参考,没有在 -v 和变量名之间放置空格会使脚本特定于 gawk,因此您应该说明这一点,或者只是在其中放置一个空格,这样它就不是 gawk 特定的。
    【解决方案5】:

    这可能对你有用(GNU sed):

     sed -n '/Host dns1/{:a;N;/^\s*$/M!ba;s/\n\s*/  /g;s/\s*$//p}' file
    

    这将重点放在所需的字符串上,然后追加以下行直到一个空白行,最后处理收集到所需输出中的新字符串。

    【讨论】:

    • sed: 1: "/Host dns1/{:a;N;/^\s*$ ...": unexpected EOF (pending }'s) 这是在 Mac 上使用 sed 的错误
    猜你喜欢
    • 2022-01-05
    • 2014-04-07
    • 2013-02-04
    • 2022-06-10
    • 2017-06-02
    • 1970-01-01
    • 2023-03-30
    • 2016-10-01
    • 1970-01-01
    相关资源
    最近更新 更多