【问题标题】:How do I add a line of text to the middle of a file using bash?如何使用 bash 在文件中间添加一行文本?
【发布时间】:2011-10-08 00:43:44
【问题描述】:

我正在尝试在 bash 脚本的文本文件中间添加一行文本。具体来说,我正在尝试将名称服务器添加到我的 /etc/resolv.conf 文件中。就目前而言,resolv.conf 看起来像这样:

# Generated by NetworkManager
domain dhcp.example.com
search dhcp.example.com
nameserver 10.0.0.1
nameserver 10.0.0.2
nameserver 10.0.0.3

我的目标是在所有其他名称服务器行之上添加nameserver 127.0.0.1,但在其之上的任何文本之下。最后,我希望我的 resolve.conf 文件看起来像这样:

# Generated by NetworkManager
domain dhcp.example.com
search dhcp.example.com
nameserver 127.0.0.1
nameserver 10.0.0.1
nameserver 10.0.0.2
nameserver 10.0.0.3

这如何通过 bash 脚本实现?这是 sed 或 awk 可以做的吗?或者创造性地重新创建文件是我最好的举措吗?

【问题讨论】:

    标签: linux bash sed awk grep


    【解决方案1】:

    这是一个使用 sed 的解决方案:

    $ sed -n 'H;${x;s/^\n//;s/nameserver .*$/nameserver 127.0.0.1\n&/;p;}' resolv.conf
    
    # Generated by NetworkManager
    domain dhcp.example.com
    search dhcp.example.com
    nameserver 127.0.0.1
    nameserver 10.0.0.1
    nameserver 10.0.0.2
    nameserver 10.0.0.3
    

    它是如何工作的:首先,使用 -n 标志抑制 sed 的输出。然后,对于每一行,我们将该行附加到保留空间,用换行符分隔它们:

    H
    

    当我们到达文件的末尾(地址为$)时,我们将保持空间的内容移动到模式空间:

    x
    

    如果模式空间中的第一行是空白的,我们将其替换为空。

    s/^\n//
    

    然后我们将第一行以nameserver 开头的行替换为包含nameserver 127.0.0.1 的行,一个新行(您的sed 版本可能不支持\n,在这种情况下,将n 替换为文字换行符)和原行(由&表示):

    s/nameserver .*$/nameserver 127.0.0.1\n&/
    

    现在我们只需要打印结果:

    p
    

    【讨论】:

    • 这很棒。唯一奇怪的是它在新文件的顶部添加了一个空行。没什么大不了的,但它会引起强迫症。
    • 哦,是的,你是对的!然而,命令sed -n 'H;${x;s/^\n//;s/nameserver .*\n/nameserver 127.0.0.1\n&/;p;}' resolv.conf 解决了这个问题。我在命令中添加了s/^\n//,因此它将删除模式空间内容开头的换行符。
    • 在另一台机器上运行相同的脚本时,只有一个名称服务器行,它不会插入任何东西。
    • 可能是因为行尾没有换行。这也很容易解决:sed -n 'H;${x;s/^\n//;s/nameserver .*$/nameserver 127.0.0.1\n&/;p;}' resolv.conf 现在我们从第一个名称服务器替换到文件末尾 - 我将/nameserver .*\n/ 中的\n 替换为行尾标记$。同样,在某些 sed 版本中,/nameserver 127.0.0.1\n&/ 中的 \n 可能必须替换为 ``,后跟实际的换行符。
    • 我受到您对帮助 sleske 的回答的启发:serverfault.com/a/745968/329412
    【解决方案2】:

    假设您想在search 行之后立即插入,这要简单得多:

    sed -ie '/^search/a nameserver 127.0.0.1' filename
    
    • -i : 就地编辑文件
    • -e :允许在 sed 表达式中执行脚本/命令
    • a mynewtext :告诉 sed 在匹配模式后插入文本 mynewtext 的命令

    【讨论】:

    • 就是这样...搜索行可能存在也可能不存在。名称服务器行应始终存在,并且我希望在它们之前立即插入新行,而不管文件的其余内容如何。
    • @PHLAK,Jim 提供的解决方案很好。不过我会修改它。只需添加一个特殊注释 来标记应该输入新内容的行。例如。像#<<INSERT>> 这样的一行。然后,您可以查找/^#<<INSERT>>$/ 而不是/^search/,这样就更不可能匹配错误的行。
    • 你会如何修改这个(如果可能的话)以使用另一个文件中的一行(假设这个文件只有一行),而不是nameserver 127.0.0.1
    • @Pom12 -e : allows the execution of a script/commands inside sed expression 是什么意思。如果我错了,请纠正我,但我相信 -e 标志没有任何作用。 AFAIK 它用于指定多个不同的 sed 命令,仅此而已。这只是 1
    【解决方案3】:

    awk '/^nameserver/ && !modif { printf("INSERT\n"); modif=1 } {print}'

    【讨论】:

    • 很棒的awk 解决方案,也许是唯一不将整个文件复制到内存的解决方案...
    • @MestreLion 你怎么看?它读取每一行,不是吗? (我也喜欢这个解决方案)
    • @DanielKaplan:它确实会读取和打印每一行,但一次一个,因此内存占用不取决于文件大小。其他解决方案可能会在找到插入点之前/如果/之后累积,这对于任意大的文件是不可取的。
    【解决方案4】:

    怎么样:

    sed -e ':a;N;$!ba;s/nameserver/nameserver 127.0.0.1\nnameserver/' /etc/resolv.conf
    

    (类似这样:sed: Find pattern over two lines, not replace after that pattern

    【讨论】:

      【解决方案5】:

      这是一个 Perl 解决方案:

      perl -lne 'if (not $f and /^nameserver/){ print "nameserver 127.0.0.1"; $f=1 }; print' resolv.conf

      • -n循环输入文件的每一行,不要自动打印每一行

      • -l 在处理之前删除换行符,然后将它们添加回来

      • -e执行perl代码

      $f 用作标志,表明名称服务器字符串已被找到

      【讨论】:

        【解决方案6】:

        这可能对你有用:

         sed -e '/nameserver/{x;/./b;x;h;i\nameserver 127.0.0.1' -e '}' resolv.conf
        

        或 GNU sed:

        sed -e '0,/nameserver/{//i\nameserver 127.0.0.1' -e '}' resolv.conf
        

        【讨论】:

          猜你喜欢
          • 2012-10-22
          • 2014-09-04
          • 1970-01-01
          • 2017-05-22
          • 2019-02-02
          • 2016-04-27
          • 2011-01-03
          • 1970-01-01
          • 2019-06-21
          相关资源
          最近更新 更多