【问题标题】:How to search and then add after a block of a multiline text with specific chars in it如何在包含特定字符的多行文本块之后搜索然后添加
【发布时间】:2021-01-06 15:45:44
【问题描述】:

如果可能的话,我需要一个通用解决方案,使用sed util 来查找多行文本块。文本事先不知道,它可能包含特定符号,所以我无法转义符号。此块必须被视为原始文本。

然后我需要在文件中插入另一个文本块,其中也可能包含不同的特定字符,事先不知道。

这是一个例子。包含多个qq§$<>ui 行的原始文件:

line1
line2
qq§$<>ui
klfd</de>
qq§$<>ui
line gg
qq§$<>ui
line aaa
qq§$<>ui
line bbb
lastButOneLine
lastLine

要搜索的文本:

qq§$<>ui
klfd</de>

后面要添加的文字:

qq§$<>ui
another2ndLine</de>combination

结果:

line1
line2
qq§$<>ui
klfd</de>
qq§$<>ui
another2ndLine</de>combination
qq§$<>ui
line gg
qq§$<>ui
line aaa
qq§$<>ui
line bbb
lastButOneLine
lastLine

【问题讨论】:

标签: regex linux shell sed


【解决方案1】:

假设ip.txt 是输入文件,f1 有要搜索的输入字符串,f2 有要添加的字符串。

使用perl(适用于给定示例,不确定其他一些 unicode 字符是否会导致问题)

a="$(< f1)" b="$(< f2)" perl -0777 -pe 's/\Q$ENV{a}\E\K/\n$ENV{b}/g' ip.txt

\Q\E 将保护输入不被解释为正则表达式元字符



对于 GNU sed,假设输入没有 ASCII NUL 字符。

$ # escape all BRE metacharacters
$ # replace literal newlines with \n
$ sed -z 's#[[^$*.\/]#\\&#g; s/\n/\\n/g' f1
qq§\$<>ui\nklfd<\/de>\n

$ # escape all replacement section metacharacters for f2
$ # and add trailing \ for literal newlines
$ sed 's:[\\/&]:\\&:g;$!s/$/\\/' f2
qq§$<>u$i\
another2ndLine<\/de>combination

工作后,您可以再次使用sed -z 进行实际修改:

$ search="$(sed -z 's#[[^$*.\/]#\\&#g; s/\n/\\n/g' f1)"
$ repl="$(sed 's:[\\/&]:\\&:g;$!s/$/\\/' f2)"
$ sed -z 's/'"$search"'/&'"$repl"'\n/g' ip.txt
line1
line2
qq§$<>ui
klfd</de>
qq§$<>u$i
another2ndLine</de>combination
qq§$<>ui
line gg
qq§$<>ui
line aaa
qq§$<>ui
line bbb
lastButOneLine
lastLine


ripgrep:

rg -N --passthru -UF "$(< f1)" -r '$0'$'\n'"$(sed 's/\$/$$/g' f2)" ip.txt
  • -N 防止输出中的行号
  • --passthru 允许打印所有输入行,无论它们是否匹配搜索条件
  • -UF启用多行匹配和固定字符串匹配
  • "$(&lt; f1)" 输入要搜索的字符串,注意后面的换行符会被移除
  • -r '$0'$'\n'"$(sed 's/\$/$$/g' f2)" 替换字符串
    • $0匹配的字符串
    • $'\n' 添加之前删除的尾随换行符
    • "$(sed 's/\$/$$/g' f2)" f2 的内容与 $ 转义为 $$

有关使用rg 命令进行搜索和替换的更多详细信息,请参阅my blog post

【讨论】:

    【解决方案2】:

    @Sundeep、@stevesliva,感谢您的努力。这两种解决方案对我来说都有点复杂,我需要一些不太复杂的解决方案。

    如果将解决方案视为一个函数,那么最简单的应该只接受 3 个参数:

    1. 文件路径,
    2. 搜索文本块,
    3. 插入文本块。

    作为消费者/客户,我不想知道如何解决,而只想知道 WHAT 传递给解决方案。

    regexp 是一个非常可行的解决方案,但如果您不定期使用它们,则需要更多时间来维护和使用它们。

    我创建了一个小型 Java 应用程序。对于我的环境,运行 java 不是问题。以下是它的调用方式:

    java -jar insert-unique-after.jar \
      --path some.txt \
      --insert-after "line1
      line2
        line3" \
      --insert-text "line4
      line5
      line6"
    

    简单明了。那是我的选择。

    对于那些想尝试的人,这里有一个 git 项目:insert-after 和一个构建的可执行 jar 文件:insert-unique-after.jar

    我确信在任何现代编程语言上实现它也非常简单,无需安装jre

    【讨论】:

      【解决方案3】:

      我倾向于不喜欢复杂的正则表达式。这并不简单,但是您可以从 grep 结果流水线构造一个简单的 sed 命令来进行替换。

      要搜索的文件是file,要添加的行是add.txt

      首先,找到所有第二行,输出中包含之前的行和行号:

      $ grep -nFB1 'klfd</de>' file
      3-qq§$<>ui
      4:klfd</de>
      

      其次,只查找第一行在第二行之前的行:

      $ grep -nFB1 'klfd</de>' file | grep -FA1 -- '-qq§$<>ui'
      3-qq§$<>ui
      4:klfd</de>
      

      第三,将该输出转换为每个匹配行的简单 sed r 命令:

      $ grep -nFB1 'klfd</de>' file | grep -FA1 -- '-qq§$<>ui' | sed -n '/^[0-9]*:/{s/:.*/r add.txt\n/;P}'
      4r add.txt
      

      最后,在filesed -f- 上运行上述命令以从标准输入获取命令。

      $ echo ADD THIS > add.txt
      
      $ grep -nFB1 'klfd</de>' file | grep -FA1 -- '-qq§$<>ui' | sed -n '/^[0-9]*:/{s/:.*/r add.txt\n/;P}' | sed -f- file
      line1
      line2
      qq§$<>ui
      klfd</de>
      ADD THIS
      qq§$<>ui
      line gg
      qq§$<>ui
      line aaa
      qq§$<>ui
      line bbb
      lastButOneLine
      lastLine
      

      【讨论】:

        猜你喜欢
        • 2022-10-20
        • 1970-01-01
        • 1970-01-01
        • 2017-08-19
        • 1970-01-01
        • 2016-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多