简体   繁体   English

在 shell 脚本中查找多行文本并使用正则表达式替换它

[英]Find multi-line text & replace it, using regex, in shell script

I am trying to find a pattern of two consecutive lines, where the first line is a fixed string and the second has a part substring I like to replace.我试图找到两个连续行的模式,其中第一行是固定字符串,第二行有一部分 substring 我喜欢替换。

This is to be done in sh or bash on macOS.这将在 macOS 上的shbash中完成。

If I had a regex tool at hand that would operate on the entire text, this would be easy for me.如果我手头有一个可以对整个文本进行操作的正则表达式工具,这对我来说很容易。 However, all I find is bash's simple text replacement - which doesn't work with regex, and sed , which is line oriented.但是,我发现的只是 bash 的简单文本替换——它不适用于正则表达式,以及sed ,它是面向行的。

I suspect that I can use sed in a way where it first finds a matching first line, and only then looks to replace the following line if its pattern also matches, but I cannot figure this out.我怀疑我可以使用sed的方式首先找到匹配的第一行,然后如果它的模式也匹配,然后才寻找替换下一行,但我无法弄清楚这一点。

Or are there other tools present on macOS that would let me do a regex-based search-and-replace over an entire file or a string?或者 macOS 上是否有其他工具可以让我对整个文件或字符串进行基于正则表达式的搜索和替换? Maybe with Python (v2.7 and v3 is installed)?也许使用 Python(安装了 v2.7 和 v3)?

Here's a sample text and how I like it modified:这是一个示例文本以及我喜欢它的修改方式:

  keyA
  value:474
  keyB
  value:474    <-- only this shall be replaced (follows "keyB")
  keyC
  value:474
  keyB
  value:474

Now, I want to find all occurances where the first line is "keyB" and the following one is "value:474", and then replace that second line with another value, eg "value:888".现在,我想找出第一行是“keyB”而下一行是“value:474”的所有情况,然后将第二行替换为另一个值,例如“value:888”。

As a regex that ignores line separators, I'd write this:作为一个忽略行分隔符的正则表达式,我会这样写:

  • Search: (\bkeyB\n\s*value):474搜索: (\bkeyB\n\s*value):474
  • Replace: $1:888更换: $1:888

So, basically, I find the pattern before the 474, and then replace it with the same pattern plus the new number 888, thereby preserving the original indentation (which is variable).所以,基本上,我找到了 474 之前的模式,然后用相同的模式加上新的数字 888 替换它,从而保留了原来的缩进(它是可变的)。

You can use您可以使用

sed -e '/keyB$/{n' -e 's/\(.*\):[0-9]*/\1:888/' -e '}' file
# Or, to replace the contents of the file inline in FreeBSD sed:
sed -i '' -e '/keyB$/{n' -e 's/\(.*\):[0-9]*/\1:888/' -e '}' file

Details :详情

  • /keyB$/ - finds all lines that end with keyB /keyB$/ - 查找所有以keyB结尾的行
  • n - empties the current pattern space and reads the next line into it n - 清空当前模式空间并将下一行读入其中
  • s/\(.*\):[0-9]*/\1:888/ - find any text up to the last : + zero or more digits capturing that text into Group 1, and replaces with the contents of the group and :888 . s/\(.*\):[0-9]*/\1:888/ - 查找直到最后的任何文本: + 零个或多个数字将该文本捕获到第 1 组中,并替换为该组的内容和:888

The {...} create a block that is executed only once the /keyB$/ condition is met. {...}创建一个仅在满足/keyB$/条件时才执行的块。

See an online sed demo .请参阅在线sed演示

Use a perl one-liner with -0777 to scan over multiple lines:使用带有-0777的 perl 单线扫描多行:

$ # inline edit:
$ perl -0777 -i -pe 's/\bkeyB\s*value):\d*/$1:888/' file.txt
$ # to stdout:
$ cat file.txt | perl -0777 -pe 's/\bkeyB\s*value):\d*/$1:888/'

In plain bash:在普通 bash 中:

#!/bin/bash

keypattern='^[[:blank:]]*keyB$'
valpattern='(.*):'
replacement=888

while read -r; do
    printf '%s\n' "$REPLY"
    if [[ $REPLY =~ $keypattern ]]; then
        read -r
        if [[ $REPLY =~ $valpattern ]]; then
            printf '%s%s\n' "${BASH_REMATCH[0]}" "$replacement"
        else
            printf '%s\n' "$REPLY"
        fi
    fi
done < file

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM