【问题标题】:Modify non-unique line in file with sed/awk使用 sed/awk 修改文件中的非唯一行
【发布时间】:2019-08-13 17:54:50
【问题描述】:

我想使用 sed 或 awk 等 bash 工具替换文件中的一行。使这变得困难的部分是该行不是唯一的,那么我该如何匹配它呢?

我确信自己可以通过其他方式完成匹配,但我只知道以下用于替换行的命令:

sed -i "s,^    AGE: .*,    AGE: ${age}," profiles.txt

profiles.txt 看起来像:

ALICE:
    AGE: 
    GRADE: 
BOB:
    GRADE: 
    AGE: 
...

如何找到特定人员的AGE: 行并进行更新?每个人的字段可以按任何顺序排列,人也可以。人们将始终拥有这两个字段,如果未知,则它们只是空白。

【问题讨论】:

    标签: regex bash shell awk sed


    【解决方案1】:

    你可以在awk中这样设置BOB的年龄

    age=10
    awk -v val=${age} '/BOB/ {f=1} f && /AGE/ {$0=$0 FS val;f=0} 1' file
    ALICE:
        AGE:
        GRADE:
    BOB:
        GRADE:
        AGE:  10
    ...
    

    它将找到BOB 并将f 设置为true。如果f 为真且找到AGE,则添加年龄并重置f

    【讨论】:

    • 理论上字符串BOB可以是任何地方,即另一个名称的一部分,即BOBBY,甚至是一个值。此外,AGE: 可能已经有一个值,不一定是空值。
    • @anubhava True,取决于 OP 输入。名称可以固定为/^BOB:$/ 之类的,以完全匹配。
    • @Jotne:如果BOB 没有年龄,但随后的ALICE 有一个年龄字段,那么您的程序将设置爱丽丝的年龄。对于以 not BOB 开头的每一行,您还需要重置 f
    • @user1934428 这是一个假设,OP 可以提供更多信息。但是由于有相等的空字段,我确实假设它都是相等的。同时,为什么BOB:不是ALICE的字段设置?由于字段前面的选项卡?这也是一个假设。
    • 我确实将它修改为使用/^BOB:$/,它确实有效。此外,就我而言,除非文件损坏,否则不会有没有 AGE 字段的学生。
    【解决方案2】:

    您可以使用此awk(假设您要将AGE 更改为BOB):

    awk -v age=20 -v name='BOB' '/^[[:alpha:]]+:/ {
       p = ($0 ~ "^" name ":")
    }
    p && /^[[:blank:]]+AGE:/ {
       sub(/AGE:.*/, "AGE: " age)
    } 1' file.yml
    

    ALICE:
        AGE:
        GRADE:
    BOB:
        GRADE:
        AGE: 20
    

    【讨论】:

    • "JOEBOB" ​​将匹配 "BOB"。
    • 感谢@DennisWilliamson,它已在脚本中修复。
    【解决方案3】:

    一个简单的sed 脚本:

    #! /bin/bash
    
    declare -u student=$1
    declare -u key=$2
    declare    val=$3
    
    sed -Ei "/^$student:/,/^\S/{ s/$key:.*/$key: $val/; }" profiles.txt
    

    执行:

    $: cat profiles.txt
    ALICE:
        AGE:
        GRADE:
    BOB:
        GRADE:
        AGE:
    
    $: update alice grade 70
    
    $: cat profiles.txt
    ALICE:
        AGE:
        GRADE: 70
    BOB:
        GRADE:
        AGE:
    

    declare -u 自动将分配给变量的字符串大写。这是一个非常简单的模式集,所以我可以只使用双引号。


    附录

    这在最后一个块上运行良好:

    $: update bob age 14
    $: cat profiles.txt
    ALICE:
        AGE:
        GRADE: 70
    BOB:
        GRADE:
        AGE: 14
    

    切换在文件末尾仍处于打开状态,因为没有任何东西将其关闭,但这在功能上是可以的。 :)

    【讨论】:

    • /^$student:/,/^\S/{ ... } 表示只在最左边一列中以给定 srudent 的大写名称开头的行之间的卷曲块中执行代码,以及最左边一列中的任何其他非空格。这是一个块敏感的应用程序。
    • 似乎这个解决方案不适用于文件中的最后一个块,但我没有测试。在我的特殊情况下,文件末尾有一个“...”,并且在其上方添加了条目,因此您的解决方案可以完美运行。让它写回文件并成为单行的道具。 ;)
    猜你喜欢
    • 2014-02-02
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 2016-10-13
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多